UNPKG

@navinc/base-react-components

Version:
266 lines (237 loc) 6.2 kB
import React from 'react' import styled from 'styled-components' import propTypes from 'prop-types' import Button from './button' import Icon from './icon.js' import Banner from './banner.js' import Copy from './copy' import LoadingDots from './loading-dots.js' import { Header } from './header.js' import CDNIllustration from './cdn-illustration.js' export const StyledCDNIllustration = styled(CDNIllustration)` margin-top: 16px; height: 180px; width: 180px; ` const Content = styled.div` align-items: center; display: flex; flex-direction: column; ${({ hasImage }) => !hasImage && ` height: 400px; justify-content: center; `} & > ${LoadingDots} { color: ${({ theme }) => theme.azure}; } ` const LoadingContent = ({ imageFilename, title, ...props }) => ( <> {!!title && <Header>{title}</Header>} <Content hasImage={!!imageFilename} {...props}> <LoadingDots /> {imageFilename && <StyledCDNIllustration filename={imageFilename} data-testid="card:loading-image" />} </Content> </> ) LoadingContent.propTypes = { className: propTypes.string, imageFilename: propTypes.string, title: propTypes.string, } LoadingContent.displayName = 'LoadingContent' const BackButton = styled(({ onClick, href, ...props }) => !(onClick || href) ? null : ( <StyledBackButton onClick={onClick} href={href} {...props}> <Icon name="actions/arrow-back" /> </StyledBackButton> ) )`` BackButton.displayName = 'BackButton' BackButton.propTypes = { onClick: propTypes.func, href: propTypes.string, } const StyledHeader = styled.div` position: relative; padding-bottom: 16px; & > *:not(:first-child) { margin-top: 4px; } ` const Label = styled(Copy).attrs(() => ({ size: 'sm' }))` color: ${({ theme }) => theme.neutral400}; ` const Title = styled(Header).attrs(() => ({ size: 'md' }))`` const StyledFooter = styled.div` position: relative; display: flex; justify-content: flex-end; align-items: center; margin-top: 24px; padding-top: 16px; background: ${({ theme }) => theme.white}; &::before { content: ''; width: 100%; height: 4px; position: absolute; top: 0; background: ${({ theme }) => theme.neutral200}; border-radius: 14px; } /* For button spacing when there are multiple buttons */ & > *:not(${BackButton}) { margin: 0 8px; } & > *:first-child:not(${BackButton}) { margin-left: 0; } & > *:last-child:not(${BackButton}) { margin-right: 0; } ` export const Svg = styled.svg` display: block; ` export const StyledBackButton = styled(Button).attrs(() => ({ variation: 'noOutline', }))` margin-right: auto; padding: 8px; color: ${({ theme }) => theme.neutral400}; ` export const CardHeader = styled(({ children, label, title, ...remainingProps }) => ( <StyledHeader {...remainingProps}> {label && <Label>{label}</Label>} {title && <Title>{title}</Title>} {children} </StyledHeader> ))`` CardHeader.propTypes = { label: propTypes.string, title: propTypes.string, } CardHeader.displayName = 'Header' export const CardFooter = styled( ({ actionText = 'Next', actionForm, actionButtonType, actionDataTestId, onAction, actionHref, isActionDisabled, isLoading, onBack, backHref, actionTrackingContext, actionTarget, children, ...remainingProps }) => ( <StyledFooter {...remainingProps}> {(!!onBack || !!backHref) && ( <BackButton data-testid="card:back" type="button" onClick={onBack} href={backHref} /> )} {children} {(!!onAction || !!actionHref || !!actionForm) && ( <Button data-testid={actionDataTestId || 'card:next'} onClick={onAction} form={actionForm} href={actionHref} isLoading={isLoading} disabled={isLoading || isActionDisabled} size="cardButton" variation="noOutline" trackingContext={actionTrackingContext} target={actionTarget} {...(actionButtonType && { type: actionButtonType })} > {actionText} </Button> )} </StyledFooter> ) )`` CardFooter.displayName = 'Footer' CardFooter.propTypes = { actionText: propTypes.node, actionForm: propTypes.string, actionDataTestId: propTypes.string, actionHref: propTypes.string, actionButtonType: propTypes.string, onAction: propTypes.func, isActionDisabled: propTypes.bool, isLoading: propTypes.bool, onBack: propTypes.func, backHref: propTypes.string, actionTrackingContext: propTypes.shape({ type: propTypes.string, context: propTypes.string, category: propTypes.string, payload: propTypes.shape({ category: propTypes.string, label: propTypes.string, name: propTypes.string, }), options: propTypes.shape({ integrations: propTypes.shape({ Salesforce: propTypes.bool, }), }), }), actionTarget: propTypes.string, } const CARD_PADDING = 24 const focusedCSS = ` text-align: center; width: 100%; & ${CardHeader} { display: flex; justify-content: center; padding-bottom: 8px; text-align: center; } & ${Title} { max-width: 400px; } ` export const Card = styled.div` position: relative; padding: ${CARD_PADDING}px; border-radius: 20px; background-color: ${({ theme }) => theme.white}; box-shadow: 0 24px 16px -16px rgba(0, 0, 0, 0.05); overflow: hidden; & ${Banner} { margin: -${CARD_PADDING}px -${CARD_PADDING}px 16px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } ${({ isFocused }) => isFocused && focusedCSS} ` Card.displayName = 'StandardCard' const FocusedContent = styled.div` align-items: center; display: flex; flex-direction: column; & > * { max-width: 400px; } ` const TermsFooter = styled.section` padding: 16px 32px; margin: 24px -24px -24px -24px; background-color: ${({ theme }) => theme.neutral100}; ` Card.Header = CardHeader Card.Footer = CardFooter Card.BackButton = BackButton Card.FocusedContent = FocusedContent Card.LoadingContent = LoadingContent Card.TermsFooter = TermsFooter export default Card