-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(card): Add Card component (#279)
* feat(card): Add Card component * Add CardContent component * Changes from design review
- Loading branch information
1 parent
4e4a606
commit 6cdfec3
Showing
5 changed files
with
427 additions
and
2 deletions.
There are no files selected for viewing
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,244 @@ | ||
import { Card, CardProps } from '.'; | ||
import { illustrations } from '../../util/images'; | ||
import Button from '../button'; | ||
import { Badge } from '../badge'; | ||
import { CheckIcon, MehIcon, PlusCircleIcon, XIcon } from '../icon'; | ||
|
||
const story = { | ||
title: 'JSX/Card', | ||
component: Card, | ||
argTypes: { | ||
density: { | ||
description: 'Spacing around the card' | ||
}, | ||
label: { | ||
description: 'Content to be rendered as label' | ||
}, | ||
title: { | ||
description: 'Content to be rendered as title' | ||
}, | ||
titleVariant: { | ||
description: 'Variant that allows changing title sizing styles.' | ||
}, | ||
description: { | ||
description: 'Content to be rendered as description' | ||
}, | ||
descriptionVariant: { | ||
description: 'Variant that allows changing description sizing styles.' | ||
}, | ||
icon: { | ||
description: 'ReactNode to be rendered on the left side of the card', | ||
}, | ||
children: { | ||
control: { type: 'text' }, | ||
description: 'Content that is displayed inside the card under the pre-defined props', | ||
}, | ||
onClick: { | ||
description: 'On click action to be triggered on card click.', | ||
}, | ||
dropShadow: { | ||
description: 'Wether to display card with drop shadow styles or not.', | ||
}, | ||
actionIcon: { | ||
description: 'ReactNode to be rendered on the right side of the card when there is an onClick action. By default it renders the ChevronRightIcon.', | ||
}, | ||
}, | ||
args: { | ||
density: 'balanced', | ||
description: 'Believe you’re a great fit but can’t find a position listed for your skill set? We’d love to hear from you!', | ||
descriptionVariant: 'large', | ||
label: 'Label', | ||
title: 'Honest, simple insurance', | ||
titleVariant: 'large', | ||
icon: 'ABC', | ||
children: '', | ||
classNames: { | ||
wrapper: '', | ||
label: '', | ||
title: '', | ||
description: '', | ||
children: '', | ||
icon: '' | ||
}, | ||
dropShadow: true, | ||
} | ||
}; | ||
|
||
export const CardStory = ({ | ||
actionIcon, | ||
children, | ||
classNames, | ||
density, | ||
description, | ||
descriptionVariant, | ||
dropShadow, | ||
icon, | ||
label, | ||
onClick, | ||
title, | ||
titleVariant, | ||
}: CardProps) => ( | ||
<div className='d-flex p24 bg-grey-200'> | ||
<Card | ||
classNames={classNames} | ||
description={description} | ||
descriptionVariant={descriptionVariant} | ||
density={density} | ||
dropShadow={dropShadow} | ||
icon={icon} | ||
label={label} | ||
title={title} | ||
titleVariant={titleVariant} | ||
onClick={onClick} | ||
actionIcon={actionIcon} | ||
> | ||
{children} | ||
</Card> | ||
</div> | ||
); | ||
|
||
CardStory.storyName = "Card"; | ||
|
||
export const CardDensities = () => ( | ||
<div className='d-flex fd-column gap16 p24 bg-grey-200'> | ||
<Card | ||
title={'Card density: Compact'} | ||
density='compact' | ||
/> | ||
<Card | ||
title={'Card density: Balanced'} | ||
density='balanced' | ||
/> | ||
<Card | ||
title={'Card density: Spacious'} | ||
density='spacious' | ||
/> | ||
</div> | ||
); | ||
|
||
export const CardsWithIcons = ({ | ||
children, | ||
icon, | ||
title, | ||
}: CardProps) => ( | ||
<div className='d-flex gap16 p24 bg-grey-200'> | ||
<Card | ||
icon={ | ||
<img | ||
alt="" | ||
src={illustrations.aids} | ||
width={24} | ||
/> | ||
} | ||
title={title} | ||
/> | ||
<Card | ||
icon={<MehIcon size={24} />} | ||
title={title} | ||
/> | ||
</div> | ||
); | ||
|
||
export const CardWithOnClickAction = ({ | ||
children, | ||
icon, | ||
title, | ||
}: CardProps) => ( | ||
<div className='d-flex p24 bg-grey-200'> | ||
<Card | ||
icon={ | ||
<img | ||
alt="" | ||
src={illustrations.aids} | ||
width={24} | ||
/> | ||
} | ||
title={title} | ||
titleVariant={'medium'} | ||
onClick={() => {}} | ||
> | ||
{children} | ||
</Card> | ||
</div> | ||
); | ||
|
||
export const CardOverridesStyles = ({ | ||
children, | ||
label, | ||
title, | ||
}: CardProps) => ( | ||
<div className='d-flex p24 bg-grey-200'> | ||
<Card | ||
label={label} | ||
title={title} | ||
titleVariant={'medium'} | ||
icon={<PlusCircleIcon color="grey-100" size={32} />} | ||
classNames={{ | ||
wrapper: 'bg-grey-700', | ||
label: 'tc-white', | ||
title: 'tc-white' | ||
}} | ||
> | ||
{children} | ||
</Card> | ||
</div> | ||
); | ||
|
||
export const CardsWithinCardsAndComplexLayout = ({ | ||
children, | ||
label, | ||
title, | ||
}: CardProps) => ( | ||
<div className='d-flex p24 bg-grey-200'> | ||
<Card | ||
label={( | ||
<Badge size='small' variant='success'> | ||
Active | ||
</Badge> | ||
)} | ||
title={( | ||
<div className='d-flex jc-between ai-center w100'> | ||
Coverage overview | ||
|
||
<Button | ||
onClick={() => {}} | ||
buttonTitle='Full coverage details' | ||
buttonType='secondaryGrey' | ||
/> | ||
</div> | ||
)} | ||
> | ||
<div className='d-flex gap16 mt16'> | ||
<Card | ||
description="Lost keys" | ||
classNames={{ wrapper: 'bg-grey-300' }} | ||
icon={<CheckIcon color={'primary-500'} />} | ||
density='compact' | ||
/> | ||
<Card | ||
description="Broken glass" | ||
classNames={{ wrapper: 'bg-grey-300' }} | ||
icon={<XIcon color={'primary-500'} />} | ||
density='compact' | ||
/> | ||
</div> | ||
|
||
<div className='d-flex gap16 mt16'> | ||
<Card | ||
description="Damage to property" | ||
classNames={{ wrapper: 'bg-grey-300' }} | ||
icon={<CheckIcon color={'primary-500'} />} | ||
density='compact' | ||
/> | ||
<Card | ||
description="Drones" | ||
classNames={{ wrapper: 'bg-grey-300' }} | ||
icon={<XIcon color={'primary-500'} />} | ||
density='compact' | ||
/> | ||
</div> | ||
</Card> | ||
</div> | ||
); | ||
|
||
export default story; |
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,144 @@ | ||
import { ReactNode } from 'react'; | ||
import classNamesUtil from 'classnames'; | ||
import { ChevronRightIcon } from '../icon'; | ||
|
||
import styles from './style.module.scss'; | ||
|
||
export interface CardProps { | ||
children?: ReactNode; | ||
classNames?: { | ||
wrapper?: string; | ||
label?: string; | ||
title?: string; | ||
description?: string; | ||
children?: string; | ||
icon?: string; | ||
actionIcon?: string; | ||
}; | ||
density?: 'balanced' | 'compact' | 'spacious'; | ||
dropShadow?: boolean; | ||
icon?: ReactNode; | ||
title?: ReactNode; | ||
titleVariant?: 'small' | 'medium' | 'large'; | ||
description?: ReactNode; | ||
descriptionVariant?: 'small' | 'large'; | ||
label?: ReactNode; | ||
onClick?: () => void; | ||
actionIcon?: ReactNode; | ||
} | ||
|
||
const CardContent = ({ | ||
children, | ||
classNames, | ||
density = 'balanced', | ||
description, | ||
descriptionVariant = 'large', | ||
dropShadow = true, | ||
icon, | ||
label, | ||
onClick, | ||
actionIcon, | ||
title, | ||
titleVariant = 'large', | ||
}: CardProps) => ( | ||
<section | ||
className={classNamesUtil( | ||
'd-flex fd-column jc-center br8 bg-white w100 ta-left', | ||
{ 'bs-sm': dropShadow }, | ||
{ | ||
compact: 'p16', | ||
balanced: 'p24', | ||
spacious: 'p32' | ||
}[density], | ||
classNames?.wrapper, | ||
)} | ||
> | ||
<div className='d-flex w100'> | ||
{icon && ( | ||
<div | ||
className={classNamesUtil( | ||
`d-flex ai-center tc-primary-500`, | ||
styles.icon, | ||
styles[`icon${density}`], | ||
classNames?.icon | ||
)} | ||
> | ||
{icon} | ||
</div> | ||
)} | ||
|
||
<div className='d-flex jc-between w100'> | ||
<div className='d-flex jc-center gap8 fd-column tc-grey-900 w100'> | ||
{label && ( | ||
<h3 | ||
className={classNamesUtil('p-p--small', classNames?.label)} | ||
> | ||
{label} | ||
</h3> | ||
)} | ||
|
||
{title && ( | ||
<h2 | ||
className={classNamesUtil(classNames?.title, { | ||
large:'p-h3', | ||
medium:'p-h4', | ||
small:'p-p', | ||
}[titleVariant])} | ||
> | ||
{title} | ||
</h2> | ||
)} | ||
|
||
{description && ( | ||
<div | ||
className={classNamesUtil( | ||
'tc-grey-600', | ||
classNames?.description, | ||
descriptionVariant === 'small' ? 'p-p--small' : 'p-p' | ||
)} | ||
> | ||
{description} | ||
</div> | ||
)} | ||
</div> | ||
|
||
{onClick && ( | ||
<div | ||
className={classNamesUtil( | ||
styles.actionIcon, | ||
classNames?.actionIcon, | ||
styles[`actionIcon${density}`], | ||
'd-flex ai-center' | ||
)} | ||
> | ||
{actionIcon || <ChevronRightIcon size={24} />} | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
|
||
{children && ( | ||
<div className={classNames?.children}>{children}</div> | ||
)} | ||
</section> | ||
); | ||
|
||
const Card = (props: CardProps) => { | ||
const { onClick } = props; | ||
|
||
if (onClick) { | ||
return ( | ||
<button | ||
className={classNamesUtil('c-pointer d-flex w100 br8', styles.button)} | ||
onClick={onClick} | ||
type="button" | ||
> | ||
<CardContent {...props} /> | ||
</button> | ||
) | ||
} | ||
|
||
return <CardContent {...props} />; | ||
} | ||
|
||
export { Card }; |
Oops, something went wrong.