-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat(web-react): Introduce Card component #1535
- Loading branch information
1 parent
028a33d
commit 0bc1fad
Showing
41 changed files
with
2,371 additions
and
0 deletions.
There are no files selected for viewing
315 changes: 315 additions & 0 deletions
315
packages/web-react/docs/stories/examples/CardComposition.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,315 @@ | ||
import React, { ElementType } from 'react'; | ||
import { | ||
Button, | ||
Card, | ||
CardBody, | ||
CardEyebrow, | ||
CardFooter, | ||
CardLink, | ||
CardMedia, | ||
CardTitle, | ||
Container, | ||
Grid, | ||
UseCardStyleProps, | ||
} from '../../../src/components'; | ||
import { MEDIA_IMAGE } from '../../../src/components/Card/demo/constants'; | ||
import { AlignmentX, Direction, Sizes } from '../../../src/constants'; | ||
import { GridColumns } from '../../../src/types'; | ||
|
||
type CardCompositionType = { | ||
cardElementType: ElementType; | ||
contentText: string; | ||
eyebrowText: string; | ||
gridCols: GridColumns; | ||
image: string; | ||
numCards: number; | ||
showContent: boolean; | ||
showEyebrow: boolean; | ||
showFooter: boolean; | ||
showMedia: boolean; | ||
showTitle: boolean; | ||
titleElementType: ElementType; | ||
titleText: string; | ||
titleWithLink: boolean; | ||
wrapInContainer: boolean; | ||
} & UseCardStyleProps; | ||
|
||
export default { | ||
title: 'Examples/Compositions', | ||
argTypes: { | ||
alignmentX: { | ||
control: 'select', | ||
description: 'Alignment inside CardFooter component.', | ||
options: [...Object.values(AlignmentX)], | ||
table: { | ||
category: 'CardFooter', | ||
defaultValue: { summary: AlignmentX.LEFT }, | ||
}, | ||
}, | ||
cardElementType: { | ||
control: 'text', | ||
name: 'elementType', | ||
description: 'Element type for the card.', | ||
table: { | ||
category: 'Card', | ||
defaultValue: { summary: 'article' }, | ||
}, | ||
}, | ||
contentText: { | ||
control: 'text', | ||
description: 'Text for the user content.', | ||
name: 'children', | ||
table: { | ||
category: 'CardBody', | ||
defaultValue: { | ||
summary: '', | ||
}, | ||
}, | ||
}, | ||
direction: { | ||
control: 'select', | ||
description: 'Direction of the card.', | ||
options: [...Object.values(Direction), 'horizontal-reversed'], | ||
table: { | ||
category: 'Card', | ||
defaultValue: { summary: Direction.VERTICAL }, | ||
}, | ||
}, | ||
eyebrowText: { | ||
control: 'text', | ||
description: 'Text for the CardEyebrow component.', | ||
name: 'children', | ||
table: { | ||
category: 'CardEyebrow', | ||
defaultValue: { summary: '' }, | ||
}, | ||
}, | ||
gridCols: { | ||
control: 'select', | ||
name: 'grid columns', | ||
description: 'Number of columns in the grid.', | ||
options: [1, 2, 3, 4, 5, 6, 12], | ||
}, | ||
hasFilledHeight: { | ||
control: 'boolean', | ||
description: 'Fill the height of the media.', | ||
table: { | ||
category: 'CardMedia', | ||
defaultValue: { summary: false }, | ||
}, | ||
}, | ||
image: { | ||
control: 'text', | ||
description: 'Image source for the CardMedia image.', | ||
name: 'image url', | ||
table: { | ||
category: 'CardMedia', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
isBoxed: { | ||
control: 'boolean', | ||
description: 'Border around the card.', | ||
table: { | ||
category: 'Card', | ||
defaultValue: { summary: false }, | ||
}, | ||
}, | ||
isExpanded: { | ||
control: 'boolean', | ||
description: 'Expand the media to fill the card. Only works when isBoxed is true.', | ||
table: { | ||
category: 'CardMedia', | ||
defaultValue: { summary: false }, | ||
}, | ||
}, | ||
isHeading: { | ||
control: 'boolean', | ||
description: 'If true, the CardTitle will render as a heading.', | ||
table: { | ||
category: 'CardTitle', | ||
defaultValue: { summary: true }, | ||
}, | ||
}, | ||
numCards: { | ||
control: 'select', | ||
name: 'number of cards', | ||
description: 'Number of cards to display.', | ||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], | ||
}, | ||
showFooter: { | ||
control: 'boolean', | ||
description: 'Show the CardFooter component.', | ||
name: 'show footer', | ||
table: { | ||
category: 'CardFooter', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
showContent: { | ||
control: 'boolean', | ||
description: 'Show the user content component.', | ||
name: 'show card content', | ||
table: { | ||
category: 'CardBody', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
showEyebrow: { | ||
control: 'boolean', | ||
description: 'Show the CardEyebrow component.', | ||
name: 'show eyebrow', | ||
table: { | ||
category: 'CardEyebrow', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
showMedia: { | ||
control: 'boolean', | ||
description: 'Show the CardMedia component.', | ||
name: 'show media', | ||
table: { | ||
category: 'CardMedia', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
showTitle: { | ||
control: 'boolean', | ||
description: 'Show the CardTitle component.', | ||
name: 'show title', | ||
table: { | ||
category: 'CardTitle', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
size: { | ||
control: 'select', | ||
description: 'Size of the media.', | ||
options: [...Object.values(Sizes), 'auto'], | ||
table: { | ||
category: 'CardMedia', | ||
defaultValue: { summary: Sizes.MEDIUM }, | ||
}, | ||
}, | ||
titleElementType: { | ||
control: 'text', | ||
name: 'elementType', | ||
description: 'Element type for the title.', | ||
table: { | ||
category: 'CardTitle', | ||
defaultValue: { summary: 'h4' }, | ||
}, | ||
}, | ||
titleText: { | ||
control: 'text', | ||
description: 'Text for the CardTitle component.', | ||
name: 'children', | ||
table: { | ||
category: 'CardTitle', | ||
defaultValue: { summary: '' }, | ||
}, | ||
}, | ||
titleWithLink: { | ||
control: 'boolean', | ||
description: 'Add a link to the CardTitle component.', | ||
name: 'title as link', | ||
table: { | ||
category: 'CardTitle', | ||
subcategory: 'Demo settings', | ||
}, | ||
}, | ||
wrapInContainer: { | ||
control: 'boolean', | ||
description: 'Wrap the card in a container.', | ||
name: 'wrap cards in container', | ||
}, | ||
}, | ||
args: { | ||
alignmentX: AlignmentX.LEFT, | ||
cardElementType: 'article', | ||
contentText: | ||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, nulla nunc varius lectus, nec rutrum justo nibh eu lectus. Ut vulputate semper dui. Fusce erat. Morbi fringilla convallis sapien. Sed ac felis. Aliquam erat volutpat. Aliquam euismod. Aenean vel lectus. Nunc imperdiet justo nec dolor.', | ||
direction: Direction.VERTICAL, | ||
eyebrowText: 'Eyebrow title', | ||
gridCols: 3, | ||
hasFilledHeight: false, | ||
image: MEDIA_IMAGE, | ||
isBoxed: false, | ||
isExpanded: false, | ||
isHeading: true, | ||
numCards: 3, | ||
showFooter: true, | ||
showContent: true, | ||
showEyebrow: true, | ||
showMedia: true, | ||
showTitle: true, | ||
size: Sizes.MEDIUM, | ||
titleElementType: 'h4', | ||
titleText: 'Card Title', | ||
titleWithLink: false, | ||
wrapInContainer: true, | ||
}, | ||
}; | ||
|
||
export const CardComposition = (args: CardCompositionType) => { | ||
const { | ||
alignmentX, | ||
cardElementType, | ||
contentText, | ||
direction, | ||
eyebrowText, | ||
gridCols, | ||
hasFilledHeight, | ||
image, | ||
isBoxed, | ||
isExpanded, | ||
isHeading, | ||
numCards, | ||
showContent, | ||
showEyebrow, | ||
showFooter, | ||
showMedia, | ||
showTitle, | ||
size, | ||
titleElementType, | ||
titleText, | ||
titleWithLink, | ||
wrapInContainer, | ||
...restProps | ||
} = args; | ||
|
||
const renderTitle = () => ( | ||
<CardTitle isHeading={isHeading} elementType={titleElementType}> | ||
{titleWithLink ? <CardLink href="#">{titleText}</CardLink> : titleText} | ||
</CardTitle> | ||
); | ||
|
||
const renderCard = () => ( | ||
<Grid cols={gridCols}> | ||
{Array.from({ length: numCards }, (_, index) => ( | ||
<Card key={index} elementType={cardElementType} {...restProps} isBoxed={isBoxed} direction={direction}> | ||
{showMedia && ( | ||
<CardMedia isExpanded={isExpanded} size={size} hasFilledHeight={hasFilledHeight}> | ||
<img src={image} alt="" /> | ||
</CardMedia> | ||
)} | ||
{(showEyebrow || showTitle || showContent) && ( | ||
<CardBody> | ||
{showEyebrow && <CardEyebrow>{eyebrowText}</CardEyebrow>} | ||
{showTitle && renderTitle()} | ||
{showContent && <p>{contentText}</p>} | ||
</CardBody> | ||
)} | ||
{showFooter && ( | ||
<CardFooter alignmentX={alignmentX}> | ||
<Button color="primary">Primary</Button> | ||
<Button color="secondary">Secondary</Button> | ||
</CardFooter> | ||
)} | ||
</Card> | ||
))} | ||
</Grid> | ||
); | ||
|
||
return wrapInContainer ? <Container>{renderCard()}</Container> : renderCard(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
'use client'; | ||
|
||
import classNames from 'classnames'; | ||
import React, { ElementType } from 'react'; | ||
import { Direction } from '../../constants'; | ||
import { useStyleProps } from '../../hooks'; | ||
import { SpiritCardProps } from '../../types'; | ||
import { useCardStyleProps } from './useCardStyleProps'; | ||
|
||
const defaultProps: Partial<SpiritCardProps> = { | ||
elementType: 'article', | ||
direction: Direction.VERTICAL, | ||
isBoxed: false, | ||
}; | ||
|
||
const Card = <T extends ElementType = 'article'>(props: SpiritCardProps<T>) => { | ||
const propsWithDefaults = { ...defaultProps, ...props }; | ||
const { elementType: ElementTag = 'article', direction, isBoxed, children, ...restProps } = propsWithDefaults; | ||
const { classProps } = useCardStyleProps({ direction, isBoxed }); | ||
const { styleProps, props: otherProps } = useStyleProps(restProps); | ||
|
||
return ( | ||
<ElementTag {...otherProps} className={classNames(classProps.root, styleProps.className)} style={styleProps.style}> | ||
{children} | ||
</ElementTag> | ||
); | ||
}; | ||
|
||
export default Card; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
'use client'; | ||
|
||
import classNames from 'classnames'; | ||
import React from 'react'; | ||
import { useStyleProps } from '../../hooks'; | ||
import { SpiritCardBodyProps } from '../../types'; | ||
import { useCardStyleProps } from './useCardStyleProps'; | ||
|
||
const CardBody = (props: SpiritCardBodyProps) => { | ||
const { children, ...restProps } = props; | ||
const { classProps } = useCardStyleProps(); | ||
const { styleProps, props: otherProps } = useStyleProps(restProps); | ||
|
||
return ( | ||
<div {...otherProps} className={classNames(classProps.body, styleProps.className)} style={styleProps.style}> | ||
{children} | ||
</div> | ||
); | ||
}; | ||
|
||
export default CardBody; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
'use client'; | ||
|
||
import classNames from 'classnames'; | ||
import React from 'react'; | ||
import { useStyleProps } from '../../hooks'; | ||
import { SpiritCardEyebrowProps } from '../../types'; | ||
import { useCardStyleProps } from './useCardStyleProps'; | ||
|
||
const CardEyebrow = (props: SpiritCardEyebrowProps) => { | ||
const { children, ...restProps } = props; | ||
const { classProps } = useCardStyleProps(); | ||
const { styleProps, props: otherProps } = useStyleProps(restProps); | ||
|
||
return ( | ||
<div {...otherProps} className={classNames(classProps.eyebrow, styleProps.className)} style={styleProps.style}> | ||
{children} | ||
</div> | ||
); | ||
}; | ||
|
||
export default CardEyebrow; |
Oops, something went wrong.