From 8944a360178964db9e909c93181581115b5b426c Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Wed, 6 Mar 2024 17:19:07 +0100 Subject: [PATCH 1/3] feat(mcc): Allow dividers for single content cards --- .../MultiContentCard/MultiContentCard.md | 10 +- ...tentCardExpandableSingleDividerExample.tsx | 104 ++++++++++++++++++ .../src/MultiContentCard/MultiContentCard.tsx | 49 ++++++--- 3 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md index fbc54dbe..1b0e282c 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md @@ -48,8 +48,16 @@ Left border can be displayed using `leftBorderVariant`. To display a border unde ### Expandable multi content card with dividers -Dividers between cards in the content can be shown using `withDividers` flag. +Dividers between all cards in the content can be shown using `withDividers` flag. ```js file="./MultiContentCardExpandableDividerExample.tsx" ``` + +### Expandable multi content card with single dividers + +To enable a divider just for a single card, use `dividerVariant` property passed to the `cards` array. + +```js file="./MultiContentCardExpandableSingleDividerExample.tsx" + +``` diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx new file mode 100644 index 00000000..9e536f2e --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import MultiContentCard, { MultiContentCardDividerVariant } from "@patternfly/react-component-groups/dist/dynamic/MultiContentCard"; +import { Button, Card, CardHeader, CardBody, CardFooter, Text, TextContent, TextVariants, Icon, TextList, TextListItem } from '@patternfly/react-core'; +import { ArrowRightIcon, BellIcon, CogIcon, LockIcon } from '@patternfly/react-icons'; + +const cards = [ + + + + Getting Started + + + + + + + + + Configure application + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + + + + + + + + + + + + + + + , + + + + + + + + Configure access + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + + + , + { + content: ( + + + + Next Steps + + + + + + + + + Configure notifications + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + + + + + + ), + dividerVariant: 'left' as MultiContentCardDividerVariant + } +]; + +export const BasicExample: React.FunctionComponent = () => ; diff --git a/packages/module/src/MultiContentCard/MultiContentCard.tsx b/packages/module/src/MultiContentCard/MultiContentCard.tsx index 28ce0273..9076711e 100644 --- a/packages/module/src/MultiContentCard/MultiContentCard.tsx +++ b/packages/module/src/MultiContentCard/MultiContentCard.tsx @@ -15,9 +15,18 @@ import clsx from 'clsx'; export type MultiContentCardBorderVariant = 'primary' | 'danger' | 'success' | 'info' | 'warning' | 'hidden'; +export type MultiContentCardDividerVariant = 'left' | 'right'; + +export interface MutliContentCardProps { + /** Card element to be displayed as a content */ + content: React.ReactElement; + /** Allows adding divider on the left/right from the card */ + dividerVariant?: MultiContentCardDividerVariant; +} + export interface MultiContentCardProps extends Omit { /** Cards to be displayed as a content */ - cards?: React.ReactElement[]; + cards?: (React.ReactElement | MutliContentCardProps)[]; /** Actions to be displayed in the expandable section */ actions?: React.ReactElement; /** Toggle text for the expandable section */ @@ -26,7 +35,7 @@ export interface MultiContentCardProps extends Omit = ({ setIsExpanded(!isExpanded); }; - const renderCards = (cards: React.ReactElement[], withDividers?: boolean) => ( + const renderCards = (cards: (React.ReactElement | MutliContentCardProps)[], withDividers?: boolean) => ( - {cards.map((card, index) => ( - <> - - {card} - - {(index + 1 < cards.length && withDividers) && ( - - )} - - ))} + {cards.map((card, index) => { + const isElement = React.isValidElement(card); + return ( + <> + {(index > 0 && !isElement && ((card as MutliContentCardProps).dividerVariant === 'left')) && ( + + )} + + {isElement ? card as React.ReactNode : (card as MutliContentCardProps).content} + + {(index + 1 < cards.length && (withDividers || !isElement && (card as MutliContentCardProps).dividerVariant === 'right')) && ( + + )} + + )})} ); From d73f50a6ae10a5c8e20ee7dc9cdf5da21175ec14 Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Wed, 6 Mar 2024 17:19:14 +0100 Subject: [PATCH 2/3] Update tests --- .../MultiContentCard.test.tsx | 8 + .../MultiContentCard.test.tsx.snap | 377 ++++++++++++++++++ 2 files changed, 385 insertions(+) diff --git a/packages/module/src/MultiContentCard/MultiContentCard.test.tsx b/packages/module/src/MultiContentCard/MultiContentCard.test.tsx index 806a0ca8..8d4a67d4 100644 --- a/packages/module/src/MultiContentCard/MultiContentCard.test.tsx +++ b/packages/module/src/MultiContentCard/MultiContentCard.test.tsx @@ -191,4 +191,12 @@ describe('MultiContentCard component', () => { expect(container.firstChild).toMatchSnapshot(); }); + it('should render multi content card with a single divider', () => { + const { container } = render(); + + expect(screen.getAllByRole('separator')).toHaveLength(1); + + expect(container.firstChild).toMatchSnapshot(); + }); + }); \ No newline at end of file diff --git a/packages/module/src/MultiContentCard/__snapshots__/MultiContentCard.test.tsx.snap b/packages/module/src/MultiContentCard/__snapshots__/MultiContentCard.test.tsx.snap index 039c3b05..c82ae923 100644 --- a/packages/module/src/MultiContentCard/__snapshots__/MultiContentCard.test.tsx.snap +++ b/packages/module/src/MultiContentCard/__snapshots__/MultiContentCard.test.tsx.snap @@ -1013,6 +1013,383 @@ exports[`MultiContentCard component should render expandable multi content card `; +exports[`MultiContentCard component should render multi content card with a single divider 1`] = ` +
+
+
+
+
+
+
+

+ Getting Started +

+
+
+
+
+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+
+
+ +
+
+
+
+
+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+
+ +
+
+
+
+
+
+
+
+

+ Next Steps +

+
+
+
+
+
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+
+
+ +
+
+
+
+`; + exports[`MultiContentCard component should render multi content card with dividers 1`] = `
Date: Thu, 7 Mar 2024 15:19:42 +0100 Subject: [PATCH 3/3] feat(mcc): Introduce enums and clean the code --- .../MultiContentCard/MultiContentCard.md | 2 +- ...ultiContentCardExpandableBorderExample.tsx | 4 +- ...tentCardExpandableSingleDividerExample.tsx | 2 +- .../MultiContentCard.test.tsx | 4 +- .../src/MultiContentCard/MultiContentCard.tsx | 66 ++++++++++++------- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md index 1b0e282c..88069d62 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md @@ -7,7 +7,7 @@ propComponents: ['MultiContentCard'] sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCard.md --- -import MultiContentCard from "@patternfly/react-component-groups/dist/dynamic/MultiContentCard"; +import MultiContentCard, { MultiContentCardDividerVariant, MultiContentCardBorderVariant } from "@patternfly/react-component-groups/dist/dynamic/MultiContentCard"; import { ArrowRightIcon, BellIcon, CogIcon, EllipsisVIcon, LockIcon } from '@patternfly/react-icons'; A **multi content card** component allows to display multiple card components in a single layout. To further customize this layout, you can also utilize all properties of the [card component](/components/card), with the exception of `children` and `title`. diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableBorderExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableBorderExample.tsx index a913f430..52b9403d 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableBorderExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableBorderExample.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import MultiContentCard from "@patternfly/react-component-groups/dist/dynamic/MultiContentCard"; +import MultiContentCard, { MultiContentCardBorderVariant } from "@patternfly/react-component-groups/dist/dynamic/MultiContentCard"; import { Button, Card, CardHeader, CardBody, CardFooter, Text, TextContent, TextVariants, Icon, TextList, TextListItem } from '@patternfly/react-core'; import { ArrowRightIcon, BellIcon, CogIcon, LockIcon } from '@patternfly/react-icons'; const cards = [ @@ -96,5 +96,5 @@ const cards = [ ]; export const BasicExample: React.FunctionComponent = () => ( - + ); diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx index 9e536f2e..04847a11 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/MultiContentCard/MultiContentCardExpandableSingleDividerExample.tsx @@ -97,7 +97,7 @@ const cards = [ ), - dividerVariant: 'left' as MultiContentCardDividerVariant + dividerVariant: MultiContentCardDividerVariant.left } ]; diff --git a/packages/module/src/MultiContentCard/MultiContentCard.test.tsx b/packages/module/src/MultiContentCard/MultiContentCard.test.tsx index 8d4a67d4..fe031a47 100644 --- a/packages/module/src/MultiContentCard/MultiContentCard.test.tsx +++ b/packages/module/src/MultiContentCard/MultiContentCard.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { screen, render } from '@testing-library/react'; import { Button, Card, CardHeader, CardBody, Text, TextContent, TextVariants, Icon, TextList, TextListItem, CardFooter, Dropdown, MenuToggle, DropdownList, DropdownItem, MenuToggleElement } from '@patternfly/react-core'; import { ArrowRightIcon, BellIcon, CogIcon, EllipsisVIcon, LockIcon } from '@patternfly/react-icons'; -import MultiContentCard from './MultiContentCard'; +import MultiContentCard, { MultiContentCardDividerVariant } from './MultiContentCard'; const cards = [ @@ -192,7 +192,7 @@ describe('MultiContentCard component', () => { }); it('should render multi content card with a single divider', () => { - const { container } = render(); + const { container } = render(); expect(screen.getAllByRole('separator')).toHaveLength(1); diff --git a/packages/module/src/MultiContentCard/MultiContentCard.tsx b/packages/module/src/MultiContentCard/MultiContentCard.tsx index 9076711e..a2c773b1 100644 --- a/packages/module/src/MultiContentCard/MultiContentCard.tsx +++ b/packages/module/src/MultiContentCard/MultiContentCard.tsx @@ -13,9 +13,23 @@ import { import { createUseStyles } from 'react-jss'; import clsx from 'clsx'; -export type MultiContentCardBorderVariant = 'primary' | 'danger' | 'success' | 'info' | 'warning' | 'hidden'; +export const MultiContentCardBorderVariant = { + primary: 'primary', + danger: 'danger', + success: 'success', + info: 'info', + warning: 'warning', + hidden: 'hidden' +} as const; -export type MultiContentCardDividerVariant = 'left' | 'right'; +export type MultiContentCardBorderVariant = typeof MultiContentCardBorderVariant[keyof typeof MultiContentCardBorderVariant]; + +export const MultiContentCardDividerVariant = { + left: 'left', + right: 'right' +} as const; + +export type MultiContentCardDividerVariant = typeof MultiContentCardDividerVariant[keyof typeof MultiContentCardDividerVariant]; export interface MutliContentCardProps { /** Card element to be displayed as a content */ @@ -56,6 +70,10 @@ const useStyles = createUseStyles({ }) }) +export const isCardWithProps = ( + card: React.ReactElement | MutliContentCardProps +): card is MutliContentCardProps => !!card && !React.isValidElement(card); + const MultiContentCard: React.FunctionComponent = ({ cards = [], isToggleRightAligned = false, @@ -63,7 +81,7 @@ const MultiContentCard: React.FunctionComponent = ({ toggleText, toggleContent, withDividers = false, - leftBorderVariant = 'hidden', + leftBorderVariant = MultiContentCardBorderVariant.hidden, isExpandable = false, defaultExpanded = true, withHeaderBorder = false, @@ -77,32 +95,30 @@ const MultiContentCard: React.FunctionComponent = ({ const renderCards = (cards: (React.ReactElement | MutliContentCardProps)[], withDividers?: boolean) => ( - {cards.map((card, index) => { - const isElement = React.isValidElement(card); - return ( - <> - {(index > 0 && !isElement && ((card as MutliContentCardProps).dividerVariant === 'left')) && ( - - )} - - {isElement ? card as React.ReactNode : (card as MutliContentCardProps).content} - - {(index + 1 < cards.length && (withDividers || !isElement && (card as MutliContentCardProps).dividerVariant === 'right')) && ( - - )} - - )})} + {cards.map((card, index) => ( + <> + {index > 0 && isCardWithProps(card) && card.dividerVariant === MultiContentCardDividerVariant.left && ( + + )} + + {isCardWithProps(card) ? card.content : card} + + {(index + 1 < cards.length && (withDividers || isCardWithProps(card) && card.dividerVariant === MultiContentCardDividerVariant.right)) && ( + + )} + + ))} ); return( - + {isExpandable && (