UNPKG

@navinc/base-react-components

Version:
231 lines (214 loc) 6.04 kB
import React from 'react' import propTypes from 'prop-types' import styled from 'styled-components' import Copy from './copy' import Icon from './icon.js' import Link from './link.js' import CDNIllustration from './cdn-illustration.js' const styles = { missingInfoAction: { primaryColor: 'paleGold500', secondaryColor: 'paleGold100', defaultIcon: 'system/search', defultActionIcon: 'actions/carrot-right', }, improveAction: { primaryColor: 'rose500', secondaryColor: 'rose100', defaultIcon: 'actions/circle-info', defultActionIcon: 'actions/carrot-right', }, positiveAction: { primaryColor: 'greenSheen500', secondaryColor: 'greenSheen100', defaultIcon: 'feedback/thumbs-up', defultActionIcon: 'actions/carrot-right', }, neutralAction: { primaryColor: 'lightBlue400', secondaryColor: 'lightBlue100', defaultIcon: 'actions/circle-info', defultActionIcon: 'actions/carrot-right', }, warning: { primaryColor: 'tuscan200', secondaryColor: 'tuscan100', defaultIcon: 'actions/circle-warning', defultActionIcon: 'actions/close', }, error: { primaryColor: 'copperRed200', secondaryColor: 'copperRed100', defaultIcon: 'actions/circle-warning', defultActionIcon: 'actions/close', }, } export const StyledBanner = styled.aside` box-shadow: 0 10px 11px -8px rgba(0, 0, 0, 0.12); position: relative; display: grid; grid-template-rows: auto 1fr; text-align: center; padding: 16px; border-radius: 12px; overflow: hidden; background-color: ${({ currentStyles, theme }) => theme[currentStyles.secondaryColor]}; cursor: ${({ hasLabel, onDismiss, hasAction }) => hasAction && !hasLabel && !onDismiss && 'pointer'}; &::before { ${({ currentStyles, shouldHideBorder, theme }) => !shouldHideBorder && `content: ''; width: 100%; height: 8px; position: absolute; left: 0; top: 0; background: ${theme[currentStyles.primaryColor]}; border-radius: 14px 14px 0 0;`} } @media (${({ theme }) => theme.forLargerThanPhone}) { grid-template-rows: auto; grid-template-columns: auto 1fr; padding-right: ${({ hasLabel }) => !hasLabel && '64px'}; text-align: left; } ` export const StyledBannerAsLink = styled(StyledBanner).attrs(() => ({ as: 'a', }))` text-decoration: none; ` const BannerContainer = ({ currentStyles, action = () => {}, actionHref, hasLabel, children, className, shouldHideBorder, 'data-testid': dataTestId, }) => { const sharedProps = { className, currentStyles, hasLabel, shouldHideBorder, 'data-testid': dataTestId, } if (!hasLabel) { return actionHref ? ( <StyledBannerAsLink {...sharedProps} href={actionHref}> {children} </StyledBannerAsLink> ) : ( <StyledBanner {...sharedProps} onClick={action}> {children} </StyledBanner> ) } else return <StyledBanner {...sharedProps}>{children}</StyledBanner> } export const TitleCopy = styled(Copy)` @media (${({ theme }) => theme.forLargerThanPhone}) { margin-right: 28px; /* need to give space for the (X) */ } ` const IconContainer = styled.div` flex: 0 0 24px; height: auto; @media (${({ theme }) => theme.forLargerThanPhone}) { margin-right: 16px; } ` const Content = styled.div` flex: 1 1 auto; & > ${Copy}, & > ${Link} { flex: 1 1 100%; } ` export const ActionIcon = styled(Icon)` cursor: pointer; position: absolute; right: 16px; top: 16px; width: auto; height: 24px; color: ${({ theme, defaultcolor = theme.neutral500, name }) => (name === 'close' ? theme.neutral500 : defaultcolor)}; ` const ChildrenWrapper = styled.div` width: '100%'; ` const StyledIcon = styled(Icon)` color: ${({ theme, color }) => theme[color]}; ` export const Banner = ({ action, actionHref, actionLabel, actionIcon, actionTarget = '', actionTrackingContext = {}, children, className, copy, expandedStyles = {}, icon, onDismiss, shouldHideBorder, CDNIllustrationIcon, title, type = 'neutralAction', }) => { const hasLabel = !!actionLabel const hasAction = !!action || !!actionHref const currentStyles = { ...styles, ...expandedStyles }[type] ?? styles.neutralAction const { primaryColor, defaultIcon, defultActionIcon } = currentStyles return ( <BannerContainer currentStyles={currentStyles} action={action} actionHref={actionHref} hasLabel={hasLabel} shouldHideBorder={shouldHideBorder} onDismiss={onDismiss} className={className} data-testid={`banner:${type}`} > <IconContainer> {CDNIllustrationIcon ? ( <CDNIllustration filename={CDNIllustrationIcon} /> ) : ( <StyledIcon name={icon || defaultIcon} color={primaryColor} data-testid="banner-icon" /> )} </IconContainer> <Content> {title && <TitleCopy bold>{title}</TitleCopy>} {copy && <Copy>{copy}</Copy>} {!!children && <ChildrenWrapper>{children}</ChildrenWrapper>} {hasLabel && hasAction && ( <Link bold href={actionHref} onClick={action} target={actionTarget} trackingContext={actionTrackingContext}> {actionLabel} </Link> )} </Content> {!onDismiss && !hasLabel && hasAction && ( <ActionIcon name={actionIcon || defultActionIcon} defaultcolor={primaryColor} data-testid="action-icon" /> )} {onDismiss && <ActionIcon onClick={onDismiss} data-testid="banner-dismiss" name="actions/close" />} </BannerContainer> ) } Banner.propTypes = { action: propTypes.func, actionHref: propTypes.string, actionLabel: propTypes.node, type: propTypes.string, icon: propTypes.string, actionIcon: propTypes.string, title: propTypes.node, copy: propTypes.node, onDismiss: propTypes.func, expandedStyles: propTypes.object, shouldHideBorder: propTypes.bool, } const StyledBannerExp = styled(Banner)`` export default StyledBannerExp