UNPKG

@navinc/base-react-components

Version:
278 lines (259 loc) 7.21 kB
import React, { useState } from 'react' import styled from 'styled-components' import Button from './button' import Copy from './copy' import Header from './header.js' import Icon from './icon.js' import Text from './text' import CDNIllustration from './cdn-illustration.js' const DetailsDrawer = styled(Button).attrs(() => ({ variation: 'noOutline' }))` display: flex; justify-content: center; align-items: center; & > ${Copy} { padding-right: 8px; } & > ${Icon} { fill: ${({ theme }) => theme.neutral400}; } ` const DisclaimerAsterisk = styled(Text)` font-size: 12px; position: absolute; ` const Flag = styled.div` display: inline-block; background-color: ${({ theme }) => theme.azure}; padding: 4px 8px; border-radius: 0 0 10px 10px; width: 128px; height: 32px; & > ${Copy} { color: ${({ theme }) => theme.white}; text-align: center; } ` const PlanPrice = styled(Copy)` position: relative; & > ${DisclaimerAsterisk} { margin-left: 2px; } ` const StyledButton = styled(Button).attrs(() => ({ variation: 'outline', size: 'large' }))`` const SelectPlanButton = styled.div` width: min-content; margin: auto; ` const NoPaddingCard = styled.div` background-color: ${({ theme }) => theme.white}; opacity: ${({ isSelected }) => (isSelected ? 0.5 : 1)}; border-radius: 4px; width: 100%; box-shadow: 0 0 3px 0 ${({ theme }) => theme.neutral300}, 0 1px 2px 0 ${({ theme }) => theme.neutral300}; ` const StripedRow = styled.div` display: flex; &::before { content: '•'; padding-right: 8px; } ` const StripedRowContent = styled(Copy).attrs(() => ({ size: 'sm' }))` display: inline-block; ` const StripedContentWrapper = styled.div` & > ${StripedRow} { padding: 16px 24px; } & > ${StripedRow}:nth-child(odd) { background-color: ${({ theme }) => theme.neutral100}; } ` const MobileStripedContentWrapper = styled(StripedContentWrapper)` @media (${({ theme }) => theme.forLargerThanPhone}) { display: none; } ` const DesktopStripedContentWrapper = styled(StripedContentWrapper)` display: none; @media (${({ theme }) => theme.forLargerThanPhone}) { display: block; } ` const PlanHeadline = styled.div` width: 100%; padding-top: 16px; ` const Illustration = styled.img` width: auto; height: auto; max-height: 100px; @media (${({ theme }) => theme.forLargerThanPhone}) { max-height: 200px; } ` const StyledCDNIllustration = styled(CDNIllustration)` width: auto; height: auto; max-height: 100px; @media (${({ theme }) => theme.forLargerThanPhone}) { max-height: 200px; } ` const PricingCardContent = styled.div` display: grid; position: relative; padding: 24px; grid-gap: 16px; grid-template: 'image plan plan' 'image plan plan' 'image description description' 'button button button' 'details details details' / auto auto auto; & > ${StyledCDNIllustration}, ${Illustration} { grid-area: image; margin: auto; padding-top: 24px; } & > ${PlanHeadline} { grid-area: plan; padding-bottom: ${({ shouldBeSelectable }) => (shouldBeSelectable ? '0px' : '24px')}; } & > ${SelectPlanButton} { grid-area: button; } & > ${Copy} { grid-area: description; } & > ${DetailsDrawer} { grid-area: details; &:hover { background-color: ${({ theme }) => theme.white}; } } & > ${Flag} { position: absolute; transform: rotateZ(-90deg); top: 44%; left: -48px; } @media (${({ theme }) => theme.forLargerThanPhone}) { grid-template-areas: 'image image image' 'plan plan plan' 'button button button' 'description description description'; & > ${PlanHeadline}, & > ${Copy} { text-align: center; } & > ${Copy} { grid-area: description; padding: 0 36px; } & > ${DetailsDrawer} { display: none; } & > ${Flag} { transform: none; top: 0; left: calc(50% - 64px); } } ` export default ({ actionHref, actionTarget, altIllustration, altIllustrationText, description, disclaimerReference, features = [], illustration, isMostPopular, isSelected, planCode, planName, planPrice, shouldBeSelectable, }) => { const [isOpen, setIsOpen] = useState(false) const renderFeatures = () => features.map((feature, i) => ( <StripedRow key={`${feature}${i}`}> <StripedRowContent> <Text>{feature}</Text> </StripedRowContent> </StripedRow> )) return ( <NoPaddingCard isSelected={isSelected}> <PricingCardContent> {isMostPopular && ( <Flag data-testid="pricing-card:is_most_popular"> <Copy>Most popular</Copy> </Flag> )} {altIllustration ? ( <Illustration src={altIllustration} alt={altIllustrationText} /> ) : ( !!illustration && <StyledCDNIllustration filename={illustration} data-testid="pricing-card:illustration" /> )} <PlanHeadline> <Header size="lg" data-testid="pricing-card:plan_name"> {planName} </Header> <PlanPrice data-testid="pricing-card:plan_price"> <strong>{planPrice}</strong> <DisclaimerAsterisk>{'*'.repeat(disclaimerReference)}</DisclaimerAsterisk> </PlanPrice> </PlanHeadline> {actionHref && shouldBeSelectable && ( <SelectPlanButton> {isSelected ? ( <StyledButton disabled data-testid="pricing-card:is_selected"> Current Plan </StyledButton> ) : ( <StyledButton href={actionHref} target={actionTarget} trackingContext={{ type: 'interaction_upgrade_page', payload: { category: 'upgrade_page_click', label: `select_${planCode}`, name: `select_${planCode}` }, }} data-testid={`pricing-card:select_this_plan_${planCode}`} > Select this plan </StyledButton> )} </SelectPlanButton> )} <Copy bold>{description}</Copy> {!!features.length && ( <DetailsDrawer trackingContext={{ type: 'interaction_upgrade_page', payload: { category: 'upgrade_page_click', label: `select_${planCode}`, name: `select_${planCode}` }, }} onClick={() => setIsOpen((isOpen) => !isOpen)} data-testid="pricing-card:mobile_see_more_details" > <Copy size="sm" light> See more details </Copy> <Icon name={isOpen ? 'actions/carrot-up' : 'actions/carrot-down'} /> </DetailsDrawer> )} </PricingCardContent> {isOpen && ( <MobileStripedContentWrapper data-testid="pricing-card-mobile-features"> {renderFeatures()} </MobileStripedContentWrapper> )} <DesktopStripedContentWrapper data-testid="pricing-card-desktop-features"> {renderFeatures()} </DesktopStripedContentWrapper> </NoPaddingCard> ) }