UNPKG

optimizely-oui

Version:

Optimizely's Component Library.

205 lines (188 loc) 6.79 kB
import React from "react"; import classNames from "classnames"; import PropTypes from "prop-types"; import Link from "../Link"; import Badge from "../Badge"; import IconLink from "./IconLink"; import CurrentUserMenu from "./CurrentUserMenu"; const LINK = "link"; const PUSH_STATE = "pushstate"; const BUTTON = "button"; const linkPropTypes = { /** CSS class names. */ className: PropTypes.string, /* Boolean, whether an external icon should show */ hasExternalIcon: PropTypes.bool, /** Should show a separator line before this link. */ hasSeparator: PropTypes.bool, /** Url to Navigate to when type is link. */ href: PropTypes.string, /** Name of Icon. */ iconName: PropTypes.string.isRequired, /** Whether the link is highlighted blue as the current active nav link. */ isActive: PropTypes.bool, /** Condition in which this link is visible. */ isVisible: PropTypes.bool, /** Description of url. */ linkLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), /** Handler called when link is clicked. */ onClick: PropTypes.func, /** Name of test data section. */ testSection: PropTypes.string, /** Type of Link. */ type: PropTypes.oneOf([LINK, PUSH_STATE, BUTTON]), }; const linkDefaultProps = { href: "", isActive: false, isVisible: true, linkLabel: "", testSection: null, hasSeparator: false, onClick: () => null, type: LINK, }; const PrimaryLink = props => <IconLink {...props} isSecondaryLink={false} />; PrimaryLink.propTypes = linkPropTypes; PrimaryLink.defaultProps = linkDefaultProps; PrimaryLink.displayName = "NavBar.PrimaryLink"; const SecondaryLink = props => <IconLink {...props} isSecondaryLink={true} />; SecondaryLink.propTypes = linkPropTypes; SecondaryLink.defaultProps = linkDefaultProps; SecondaryLink.displayName = "NavBar.SecondaryLink"; /** * * @param {Object} props - Properties passed to component * @returns {ReactElement} */ const NavBar = ({ badgeText, children, className, footerContent, homeUrl, isOpen, logoUrl, title, trialContent, ...props }) => { const renderChildrenByType = ComponentType => React.Children.toArray(children) .filter(child => child.type === ComponentType) .map(element => React.cloneElement(element, { isOpen })); const logoClasses = classNames("push-double--left", { "root-nav__logo--full": isOpen, "root-nav__logo--mark": !isOpen, }); const renderPrivacy = () => { let year = new Date().getFullYear(); return ( <div className="root-nav__user root-nav__link root-nav__link--plain hard--bottom muted"> <span className="display--inline-block"> ©2010&ndash;{year} Optimizely.{" "} <a href="https://www.optimizely.com/privacy/" className="muted underline"> Privacy </a> </span> </div> ); }; const renderFooterContent = () => { return ( <React.Fragment> <hr className="oui-rule push-double--sides hard" /> <div className="root-nav__link root-nav__link--plain flex flex--column flex--1">{footerContent}</div> </React.Fragment> ); }; return ( <nav className={classNames({ "root-nav": true, "root-nav--open": isOpen, }, className)} data-test-section="p13n-root-navbar" {...props} > <div data-test-section="navbar-header"> {logoUrl  && ( <div className="root-nav__logo push--bottom"> <Link href={homeUrl}> <img alt="logo" src={logoUrl} className={logoClasses} /> </Link> </div> )} {trialContent} <div className={classNames({ "root-nav__project": true, "root-nav-fader": !isOpen, })} > <div className="epsilon truncate" data-test-section="project-name"> {title} </div> <Badge color="purple" testSection="project-badge"> {badgeText} </Badge> </div> </div> <ul className="push--ends">{renderChildrenByType(PrimaryLink)}</ul> <div className="anchor--bottom"> <ul className="push--ends">{renderChildrenByType(SecondaryLink)}</ul> {renderChildrenByType(CurrentUserMenu)} {isOpen && footerContent && renderFooterContent()} {isOpen && renderPrivacy()} </div> </nav> ); }; NavBar.propTypes = { /** Text to appear below title in a badge. */ badgeText: PropTypes.string, /** Allowed Children are <NavBar.PrimaryLink>, <NavBar.SecondaryLink>, <NavBar.CurrentUserMenu>. Only one of <NavBar.CurrentUserMenu> is allowed. */ children: (props, propName) => { const prop = props[propName]; let error = null; React.Children.forEach(prop, child => { if (child === null) { // Skip checking empty child nodes return; } if (![SecondaryLink, PrimaryLink, CurrentUserMenu].includes(child.type)) { error = new Error("Children should be of type PrimaryLink, SecondaryLink, or CurrentUserMenu."); } }); if (!error && React.Children.toArray(prop).filter(child => child.type === CurrentUserMenu).length !== 1) { error = new Error("There should be only one instance of `CurrentUserMenu`"); } return error; }, /** CSS class names. */ className: PropTypes.string, /** Component to appear above the privacy statement. */ footerContent: PropTypes.node, /** Url to navigate to when Brand Logo is clicked. */ homeUrl: PropTypes.string, /** Is Navigation Bar open or closed. */ isOpen: PropTypes.bool, /** Brand logo URL. */ logoUrl: PropTypes.string, /** Title of navigation bar */ title: PropTypes.string, /** Component to appear above title and below brand logo. */ trialContent: PropTypes.node, }; NavBar.defaultProps = { badgeText: "", homeUrl: "", isOpen: true, logoUrl: null, title: "", trialContent: null, }; NavBar.PrimaryLink = PrimaryLink; NavBar.SecondaryLink = SecondaryLink; NavBar.CurrentUserMenu = CurrentUserMenu; export default NavBar;