Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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"

```
Original file line number Diff line number Diff line change
@@ -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 = [
Expand Down Expand Up @@ -96,5 +96,5 @@ const cards = [
];

export const BasicExample: React.FunctionComponent = () => (
<MultiContentCard isExpandable withHeaderBorder toggleText='Card with border toggle text' leftBorderVariant="primary" cards={cards} />
<MultiContentCard isExpandable withHeaderBorder toggleText='Card with border toggle text' leftBorderVariant={MultiContentCardBorderVariant.primary} cards={cards} />
);
Original file line number Diff line number Diff line change
@@ -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 = [
<Card isFullHeight isPlain key="card-1">
<CardHeader>
<TextContent>
<Text component={TextVariants.h4}>Getting Started</Text>
</TextContent>
</CardHeader>
<CardBody>
<TextContent>
<Text className="pf-v5-u-font-size-sm pf-v5-u-font-weight-bold pf-v5-u-mb-sm pf-v5-u-link-color-hover">
<Icon size="md" className="pf-v5-u-pl-sm pf-v5-u-pr-md">
<CogIcon />
</Icon>
Configure application
</Text>
<Text className="pf-v5-u-font-size-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</Text>
</TextContent>
</CardBody>
<CardFooter>
<TextContent>
<TextList className="pf-v5-u-font-size-sm pf-v5-u-link-color pf-v5-u-ml-0">
<TextListItem>
<Button variant="link" isInline>First link</Button>
</TextListItem>
<TextListItem>
<Button variant="link" isInline>Second link</Button>
</TextListItem>
<TextListItem>
<Button variant="link" isInline>Another link</Button>
</TextListItem>
</TextList>
</TextContent>
</CardFooter>
</Card>,
<Card isFullHeight isPlain key="card-2">
<CardBody className="pf-v5-u-pt-3xl-on-md">
<TextContent>
<Text className="pf-v5-u-font-size-sm pf-v5-u-font-weight-bold pf-v5-u-mb-sm pf-v5-u-link-color-hover">
<Icon size="md" className="pf-v5-u-pl-sm pf-v5-u-pr-md">
<LockIcon />
</Icon>
Configure access
</Text>
<Text className="pf-v5-u-font-size-sm">
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.
</Text>
</TextContent>
</CardBody>
<CardFooter>
<Text>
<Button variant="link" isInline>
Learn more
<Icon className="pf-u-ml-sm" isInline>
<ArrowRightIcon />
</Icon>
</Button>
</Text>
</CardFooter>
</Card>,
{
content: (
<Card isFullHeight isPlain key="card-3">
<CardHeader>
<TextContent>
<Text component={TextVariants.h4}>Next Steps</Text>
</TextContent>
</CardHeader>
<CardBody>
<TextContent>
<Text className="pf-v5-u-font-size-sm pf-v5-u-font-weight-bold pf-v5-u-mb-sm pf-v5-u-link-color-hover">
<Icon size="md" className="pf-v5-u-pl-sm pf-v5-u-pr-md">
<BellIcon />
</Icon>
Configure notifications
</Text>
<Text className="pf-v5-u-font-size-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</Text>
</TextContent>
</CardBody>
<CardFooter>
<Text>
<Button variant="link" isInline>
Learn more
<Icon className="pf-u-ml-sm" isInline>
<ArrowRightIcon />
</Icon>
</Button>
</Text>
</CardFooter>
</Card>
),
dividerVariant: MultiContentCardDividerVariant.left
}
];

export const BasicExample: React.FunctionComponent = () => <MultiContentCard isExpandable toggleText='Card with dividers toggle text' cards={cards} />;
10 changes: 9 additions & 1 deletion packages/module/src/MultiContentCard/MultiContentCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
<Card isFullHeight isPlain key="card-1">
Expand Down Expand Up @@ -191,4 +191,12 @@ describe('MultiContentCard component', () => {
expect(container.firstChild).toMatchSnapshot();
});

it('should render multi content card with a single divider', () => {
const { container } = render(<MultiContentCard cards={[ cards[0], cards[1], { content: cards[2], dividerVariant: MultiContentCardDividerVariant.left } ]} />);

expect(screen.getAllByRole('separator')).toHaveLength(1);

expect(container.firstChild).toMatchSnapshot();
});

});
49 changes: 41 additions & 8 deletions packages/module/src/MultiContentCard/MultiContentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,34 @@ 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 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 */
content: React.ReactElement;
/** Allows adding divider on the left/right from the card */
dividerVariant?: MultiContentCardDividerVariant;
}

export interface MultiContentCardProps extends Omit<CardProps, 'children' | 'title'> {
/** 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 */
Expand All @@ -26,7 +49,7 @@ export interface MultiContentCardProps extends Omit<CardProps, 'children' | 'tit
toggleContent?: React.ReactElement;
/** Left border variant for the containing card */
leftBorderVariant?: MultiContentCardBorderVariant;
/** Indicates whether the content is separated by dividers */
/** When set to true, all content cards will be separated with dividers */
withDividers?: boolean;
/** Indicates whether the card is expandable */
isExpandable?: boolean;
Expand All @@ -47,14 +70,18 @@ const useStyles = createUseStyles({
})
})

export const isCardWithProps = (
card: React.ReactElement | MutliContentCardProps
): card is MutliContentCardProps => !!card && !React.isValidElement(card);

const MultiContentCard: React.FunctionComponent<MultiContentCardProps> = ({
cards = [],
isToggleRightAligned = false,
actions,
toggleText,
toggleContent,
withDividers = false,
leftBorderVariant = 'hidden',
leftBorderVariant = MultiContentCardBorderVariant.hidden,
isExpandable = false,
defaultExpanded = true,
withHeaderBorder = false,
Expand All @@ -66,14 +93,20 @@ const MultiContentCard: React.FunctionComponent<MultiContentCardProps> = ({
setIsExpanded(!isExpanded);
};

const renderCards = (cards: React.ReactElement[], withDividers?: boolean) => (
const renderCards = (cards: (React.ReactElement | MutliContentCardProps)[], withDividers?: boolean) => (
<Flex alignSelf={{ default: 'alignSelfStretch' }} alignItems={{ default: 'alignItemsStretch' }}>
{cards.map((card, index) => (
<>
{index > 0 && isCardWithProps(card) && card.dividerVariant === MultiContentCardDividerVariant.left && (
<Divider
orientation={{ md: 'vertical' }}
inset={{ default: 'inset3xl' }}
/>
)}
<FlexItem key={`card-${index}`} flex={{ default: 'flex_1' }}>
{card}
{isCardWithProps(card) ? card.content : card}
</FlexItem>
{(index + 1 < cards.length && withDividers) && (
{(index + 1 < cards.length && (withDividers || isCardWithProps(card) && card.dividerVariant === MultiContentCardDividerVariant.right)) && (
<Divider
orientation={{ md: 'vertical' }}
inset={{ default: 'inset3xl' }}
Expand All @@ -85,7 +118,7 @@ const MultiContentCard: React.FunctionComponent<MultiContentCardProps> = ({
);

return(
<Card className={clsx([ { [classes.multiContentCardLeftBorder]: leftBorderVariant !== 'hidden' } ])} isExpanded={isExpanded} {...rest}>
<Card className={clsx([ { [classes.multiContentCardLeftBorder]: leftBorderVariant !== MultiContentCardBorderVariant.hidden } ])} isExpanded={isExpanded} {...rest}>
{isExpandable && (
<CardHeader
className={clsx({ [classes.multiContentCardHeadingBorder]: withHeaderBorder })}
Expand Down
Loading