diff --git a/package.json b/package.json index 19861967..9d4bad1f 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "polished": "^3.6.4", "prismjs": "1.20.0", "prop-types": "^15.5.4", + "react-github-button": "^0.1.11", "react-modal": "^3.11.2", "react-popper-tooltip": "^2.11.1", "styled-components": "^4.4.1", diff --git a/src/components/header/Header.stories.js b/src/components/header/Header.stories.js new file mode 100644 index 00000000..aa5f56c9 --- /dev/null +++ b/src/components/header/Header.stories.js @@ -0,0 +1,271 @@ +import React from 'react'; +import styled from 'styled-components'; +import GitHubButton from 'react-github-button'; +import 'react-github-button/assets/style.css'; +import { Header } from './Header'; +import { Link } from '../Link'; +import { Button } from '../Button'; +import StorybookLogo from '../../images/logo-storybook.svg'; +import StorybookLogoInverted from '../../images/logo-storybook-inverted.svg'; +import { breakpoint, typography, color } from '../shared/styles'; +import { NavLink } from './NavLink'; +import { NavItem } from './NavItem'; +import { TooltipLinkList } from '../tooltip/TooltipLinkList'; +import WithTooltip from '../tooltip/WithTooltip'; +import { Icon } from '../Icon'; +import { StoryLinkWrapper } from '../StoryLinkWrapper'; + +export default { + title: 'Design System/Header', + component: Header, + subcomponents: { NavLink, NavItem }, + parameters: { layout: 'fullscreen' }, +}; + +const LogoWrapper = styled.div` + display: inline-block; + align-self: stretch; +`; + +const LogotypeWrapper = styled(Link)` + display: inline-block; + + img { + height: 22px; + width: auto; + margin-top: 14px; + @media (min-width: ${breakpoint}px) { + height: 26px; + margin-top: 10px; + } + display: block; + transition: all 150ms ease-out; + transform: translate3d(0, 0, 0); + &:hover { + transform: translate3d(0, -1px, 0); + } + &:active { + transform: translate3d(0, 0, 0); + } + } +`; + +const Version = styled(Link)` + display: inline-block; + vertical-align: top; + margin-left: 10px; + position: relative; + top: 2px; + font-size: ${typography.size.s1}px; + color: ${color.mediumdark}; +`; + +const Logo = () => ( + + + Storybook + + v6.0 + +); + +const links = ( + <> + + Docs + + + Tutorials + + + Releases + + + Addons + + +); + +const mobileMenu = ( + +); + +export const Basic = () =>
} links={links} mobileMenu={mobileMenu} />; + +export const WithLinkWrapper = () => ( +
} + links={ + <> + + + Docs + + + + + Tutorials + + + + + Releases + + + + + Addons + + + + } + mobileMenu={mobileMenu} + /> +); + +export const WithGithubButton = () => ( +
} + links={links} + mobileMenu={mobileMenu} + github={} + /> +); + +export const WithDropDown = () => ( +
} + links={ + <> + {links} + + + } + > + + Community + + + + + } + mobileMenu={mobileMenu} + /> +); + +export const WithCustomLink = () => ( +
} + links={ + <> + {links} + + + Go to app + + + + } + mobileMenu={mobileMenu} + /> +); + +const InvertedLogotypeWrapper = styled(Link)` + display: inline-block; + + img { + height: 22px; + width: auto; + + @media (min-width: ${breakpoint}px) { + height: 26px; + } + display: block; + transition: all 150ms ease-out; + transform: translate3d(0, 0, 0); + &:hover { + transform: translate3d(0, -1px, 0); + } + &:active { + transform: translate3d(0, 0, 0); + } + } +`; + +export const Inverted = () => ( +
+
+ Learn Storybook + + } + links={ + <> + + Docs + + + Tutorials + + + Releases + + + Addons + + + + + + } + mobileMenu={mobileMenu} + /> +
+); diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx new file mode 100644 index 00000000..ba3b878f --- /dev/null +++ b/src/components/header/Header.tsx @@ -0,0 +1,116 @@ +import React from 'react'; +import styled from 'styled-components'; +import { color } from '../shared/styles'; +// @ts-ignore +import WithTooltip from '../tooltip/WithTooltip'; +// @ts-ignore +import { Link } from '../Link'; +// @ts-ignore +import { Icon } from '../Icon'; +import { NavGroup, Nav, NavWrapper } from './Nav'; +import { NavItem } from './NavItem'; +import { HeaderContext, defaultHeaderContext } from './HeaderContext'; + +const GitHubWrapper = styled.div` + transform: scale(0.84); + ${'' /* Overrides to make a medium sized button */}; + .github-btn { + font: bold 14px/14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + height: auto; + .gh-btn, + .gh-count { + padding: 4px 8px; + } + } +`; + +const GithubNavItem = styled(NavItem)` + display: none; + + @media (min-width: 375px) { + display: inline-flex; + align-items: center; + } +`; + +const MobileMenuTooltip = styled(WithTooltip)` + outline: none; + &:focus svg { + color: ${color.secondary}; + } +`; + +const Menu = styled(Link)` + width: 3rem; + border: none !important; + text-decoration: none !important; + svg { + height: 18px; + width: 18px; + margin: 0; + margin-right: -4px !important; + } +`; + +type HeaderProps = { + logo: React.ReactNode; + github?: React.ReactNode; + links: React.ReactNode; + mobileMenu: React.ReactNode | (() => React.ReactNode); + inverse?: boolean; + navBreakpoint?: number; +}; + +export function Header({ + logo, + links, + github, + mobileMenu, + inverse = defaultHeaderContext.inverse, + navBreakpoint = defaultHeaderContext.navBreakpoint, + ...props +}: HeaderProps) { + return ( + + + + + + ); +} + +Header.defaultProps = { + inverse: false, +}; diff --git a/src/components/header/HeaderContext.ts b/src/components/header/HeaderContext.ts new file mode 100644 index 00000000..2aa035ab --- /dev/null +++ b/src/components/header/HeaderContext.ts @@ -0,0 +1,14 @@ +import { createContext } from 'react'; +import { breakpoint } from '../shared/styles'; + +interface HeaderContextInterface { + navBreakpoint: number; + inverse: boolean; +} + +export const defaultHeaderContext = { + navBreakpoint: breakpoint * 1.25, + inverse: false, +} + +export const HeaderContext = createContext(defaultHeaderContext); diff --git a/src/components/header/Nav.tsx b/src/components/header/Nav.tsx new file mode 100644 index 00000000..807782bc --- /dev/null +++ b/src/components/header/Nav.tsx @@ -0,0 +1,49 @@ +import styled, { css } from 'styled-components'; +import { pageMargins, breakpoint } from '../shared/styles'; +import { StyledNavItem } from './NavItem'; + +export const Nav = styled.div` + height: 3rem; + position: relative; + text-align: center; + z-index: 3; +`; + +export const NavWrapper = styled.nav` + ${pageMargins} + + padding-top: 12px; + + @media (min-width: ${breakpoint}px) { + padding-top: 36px; + } +`; + +type NavGroupProps = { + withRightAlignment?: boolean; +}; + +export const NavGroup = styled.div` + position: absolute; + top: 0; + left: 0; + z-index: 1; + display: flex; + + ${(props) => + props.withRightAlignment && + css` + left: auto; + right: 0; + `} + + @media (min-width: ${(props) => props.navBreakpoint}px) { + ${StyledNavItem} + ${StyledNavItem} { + margin-left: 25px; + } + } +`; + +NavGroup.defaultProps = { + withRightAlignment: false, +}; diff --git a/src/components/header/NavItem.tsx b/src/components/header/NavItem.tsx new file mode 100644 index 00000000..faff7fe5 --- /dev/null +++ b/src/components/header/NavItem.tsx @@ -0,0 +1,43 @@ +import React, { forwardRef, useContext } from 'react'; +import styled, { css } from 'styled-components'; +import { HeaderContext } from './HeaderContext'; + +type StyledNavItemProps = { + showDesktop?: boolean; + showMobile?: boolean; +}; + +export const StyledNavItem = styled.div` + display: inline-flex; + height: 3rem; + line-height: 3rem; + align-items: center; + + svg { + margin-right: 0; + } + + ${(props) => + props.showDesktop && + css` + display: none; + @media (min-width: ${props.navBreakpoint}px) { + display: inline-flex; + } + `} + + ${(props) => + props.showMobile && + css` + @media (min-width: ${props.navBreakpoint}px) { + display: none; + } + `} +`; + +type NavItemProps = React.ComponentPropsWithoutRef<'div'> & StyledNavItemProps; + +export const NavItem = forwardRef((props, ref) => { + const { navBreakpoint } = useContext(HeaderContext); + return ; +}); diff --git a/src/components/header/NavLink.tsx b/src/components/header/NavLink.tsx new file mode 100644 index 00000000..1ff1c7fa --- /dev/null +++ b/src/components/header/NavLink.tsx @@ -0,0 +1,21 @@ +import React, { forwardRef, useContext } from 'react'; +import styled from 'styled-components'; +import PropTypes from 'prop-types'; +// @ts-ignore +import { Link } from '../Link'; +import { typography } from '../shared/styles'; +import { HeaderContext } from './HeaderContext'; + +const StyledNavLink = styled(Link)` + font-size: ${typography.size.s2}px; + font-weight: ${typography.weight.extrabold}; +`; + +type NavLinkProps = PropTypes.InferProps; + +export const NavLink = forwardRef( + (props, ref) => { + const { inverse } = useContext(HeaderContext); + return ; + } +); diff --git a/src/components/index.js b/src/components/index.js index 18b4ed9b..cfdff00c 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -38,3 +38,7 @@ export * from './ShadowBoxCTA'; export * from './Clipboard'; export * from './LinkTabs'; export * from './CodeSnippets'; + +export * from './header/Header'; +export * from './header/NavLink'; +export { NavItem } from './header/NavItem'; diff --git a/src/images/logo-storybook-inverted.svg b/src/images/logo-storybook-inverted.svg new file mode 100644 index 00000000..dc5ed5c4 --- /dev/null +++ b/src/images/logo-storybook-inverted.svg @@ -0,0 +1 @@ + diff --git a/src/images/logo-storybook.svg b/src/images/logo-storybook.svg new file mode 100644 index 00000000..b1f0f43b --- /dev/null +++ b/src/images/logo-storybook.svg @@ -0,0 +1 @@ +storybook-logo/defaultCreated with Sketch. \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ac448397..b9c5d5bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12034,6 +12034,13 @@ react-fast-compare@^3.0.1: resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.1.1.tgz#0becf31e3812fa70dc231e259f40d892d4767900" integrity sha512-SCsAORWK59BvauR2L1BTdjQbJcSGJJz03U0awektk2hshLKrITDDFTlgGCqIZpTDlPC/NFlZee6xTMzXPVLiHw== +react-github-button@^0.1.11: + version "0.1.11" + resolved "https://registry.npmjs.org/react-github-button/-/react-github-button-0.1.11.tgz#fc61e1f1e1371169d3618c1ba37306ba04081e56" + integrity sha1-/GHh8eE3EWnTYYwbo3MGugQIHlY= + dependencies: + prop-types "^15.5.10" + react-helmet-async@^1.0.2: version "1.0.6" resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.6.tgz#11c15c74e79b3f66670c73779bef3e0e352b1d4e"