From 2033456b63ce52044aded1df2d584514b41985de Mon Sep 17 00:00:00 2001 From: rogermparent Date: Mon, 20 Jul 2020 19:17:32 -0400 Subject: [PATCH 1/2] Refactor header and fix banner+placeholder positioning issues This commit primarily aims to make the Header placeholder grow and shrink while accounting for the extra height of the alert banner, fixing many positioning issues that were introduced with the banner addition. This commit also refactors the logic of the Hamburger menu into a separate React hook and a separate component for both the Hamburger menu and button. --- src/components/HamburgerMenu/index.tsx | 406 +++++++++--------- src/components/LayoutHeader/alert.tsx | 25 ++ src/components/LayoutHeader/index.tsx | 93 ++-- src/components/LayoutHeader/styles.module.css | 19 +- src/components/MainLayout/index.tsx | 2 - src/components/Page/base.css | 1 + src/utils/front/hamburgerMenu.ts | 52 +++ 7 files changed, 340 insertions(+), 258 deletions(-) create mode 100644 src/components/LayoutHeader/alert.tsx create mode 100644 src/utils/front/hamburgerMenu.ts diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx index 9a2a76d850..a92387ef7b 100644 --- a/src/components/HamburgerMenu/index.tsx +++ b/src/components/HamburgerMenu/index.tsx @@ -1,232 +1,216 @@ import cn from 'classnames' -import React, { useCallback, useState, useEffect } from 'react' +import React, { MouseEvent, KeyboardEvent } from 'react' import HamburgerIcon from '../HamburgerIcon' +import { HamburgerHelpers } from '../../utils/front/hamburgerMenu' import Link from '../Link' -import { logEvent } from '../../utils/front/ga' import { getFirstPage } from '../../utils/shared/sidebar' import { ReactComponent as LogoSVG } from '../../../static/img/logo-white.svg' import { ReactComponent as TwitterIcon } from '../SocialIcon/twitter.svg' import { ReactComponent as GithubIcon } from '../SocialIcon/github.svg' -import { useHeaderIsScrolled } from '../../utils/front/scroll' import styles from './styles.module.css' const docsPage = getFirstPage() -const HamburgerMenu: React.FC = () => { - const [isOpened, setOpened] = useState(false) - const collapsed = useHeaderIsScrolled() - - const toggleMobileMenu = useCallback(() => setOpened(!isOpened), [isOpened]) - const openOnEnterKey = useCallback(e => { - if (e.which === 13) { - toggleMobileMenu() - } - }, []) - - const close = useCallback(() => setOpened(false), [isOpened]) - const itemClick = useCallback( - item => (): void => { - close() - logEvent('hamburger', item) - }, - [] - ) - - useEffect(() => { - const method = isOpened ? 'add' : 'remove' - - document.body.classList[method](styles.hiddenScrollbar) - }, [isOpened]) - +export const HamburgerMenu: React.FC< + Pick< + HamburgerHelpers, + 'opened' | 'handleItemClick' | 'handleKeyDown' | 'handleToggle' + > & { + collapsed: boolean + } +> = ({ opened, handleItemClick }) => { return ( -
- - -
-
- - - -
-
    -
  • - - Features - -
  • -
  • - - Doc - -
  • -
  • - - Blog - -
  • -
  • - - Community - -
      -
    • - - - Meet Us - -
    • -
    • - - - Contribute - -
    • -
    • - - - Learn - -
    • -
    • - - - Events - -
    • -
    -
  • -
  • - - Support - -
      -
    • - - - E-Mail - -
    • -
    • - - - GitHub - -
    • -
    • - - - Discord - -
    • -
    • - - - Twitter - -
    • -
    -
  • -
- - Get started +
+
+ +
+
    +
  • + + Features + +
  • +
  • + + Doc + +
  • +
  • + + Blog + +
  • +
  • + + Community + +
      +
    • + + + Meet Us + +
    • +
    • + + + Contribute + +
    • +
    • + + + Learn + +
    • +
    • + + + Events + +
    • +
    +
  • +
  • + + Support + +
      +
    • + + + E-Mail + +
    • +
    • + + + GitHub + +
    • +
    • + + + Discord + +
    • +
    • + + + Twitter + +
    • +
    +
  • +
+ + Get started +
) } -export default HamburgerMenu +export const HamburgerButton: React.FC<{ + opened: boolean + collapsed: boolean + handleClick: (e: MouseEvent) => void + handleKeyDown: (e: KeyboardEvent) => void +}> = ({ opened, collapsed, handleClick, handleKeyDown }) => ( + +) diff --git a/src/components/LayoutHeader/alert.tsx b/src/components/LayoutHeader/alert.tsx new file mode 100644 index 0000000000..4c3efeb05e --- /dev/null +++ b/src/components/LayoutHeader/alert.tsx @@ -0,0 +1,25 @@ +import cn from 'classnames' +import React from 'react' + +import { ReactComponent as GitHubIcon } from '../SocialIcon/github.svg' +import Link from '../Link' + +import styles from './styles.module.css' + +const LayoutAlert: React.FC<{ collapsed: boolean }> = ({ collapsed }) => ( +
+ + 🚀 + {' '} + Check out our newest tool, CML!{' '} + + + +
+) + +export default LayoutAlert diff --git a/src/components/LayoutHeader/index.tsx b/src/components/LayoutHeader/index.tsx index b8efa526c6..5a18d1701b 100644 --- a/src/components/LayoutHeader/index.tsx +++ b/src/components/LayoutHeader/index.tsx @@ -3,61 +3,72 @@ import React from 'react' import includes from 'lodash/includes' import { LayoutModifiers, ILayoutModifiable } from '../MainLayout' -import { ReactComponent as GitHubIcon } from '../SocialIcon/github.svg' import LayoutWidthContainer from '../LayoutWidthContainer' import Link from '../Link' import Nav from './Nav' +import { HamburgerMenu, HamburgerButton } from '../HamburgerMenu' import { useHeaderIsScrolled } from '../../utils/front/scroll' import { ReactComponent as LogoSVG } from '../../../static/img/logo.svg' import styles from './styles.module.css' +import { useHamburgerMenu } from '../../utils/front/hamburgerMenu' + +import LayoutAlert from './alert' + const LayoutHeader: React.FC> = ({ modifiers }) => { + const { + opened, + handleToggle, + handleKeyDown, + handleItemClick + } = useHamburgerMenu() + const scrolled = useHeaderIsScrolled() const hasCollapsedModifier = includes(modifiers, LayoutModifiers.Collapsed) - const collapsed = hasCollapsedModifier || useHeaderIsScrolled() + const collapsed = opened || hasCollapsedModifier || scrolled return ( - + + + ) } diff --git a/src/components/LayoutHeader/styles.module.css b/src/components/LayoutHeader/styles.module.css index 4803d582b2..9456bb5e3d 100644 --- a/src/components/LayoutHeader/styles.module.css +++ b/src/components/LayoutHeader/styles.module.css @@ -1,12 +1,23 @@ .placeholder { + transition: height 0.2s linear; height: var(--layout-header-height); - &.collapsed { - height: var(--layout-header-height-collapsed); + &.withAlert { + height: calc(var(--layout-header-height) + var(--layout-alert-height)); } @media (--xs-scr) { height: var(--layout-header-height-collapsed); + + &.withAlert { + height: calc( + var(--layout-header-height-collapsed) + var(--layout-alert-height) + ); + } + } + + &.collapsed { + height: var(--layout-header-height-collapsed); } } @@ -61,8 +72,8 @@ } .alert { - height: 35px; - line-height: 35px; + height: var(--layout-alert-height); + line-height: var(--layout-alert-height); font-size: 18px; text-align: center; width: 100%; diff --git a/src/components/MainLayout/index.tsx b/src/components/MainLayout/index.tsx index 41ab52505e..8fa76ef0f8 100644 --- a/src/components/MainLayout/index.tsx +++ b/src/components/MainLayout/index.tsx @@ -2,7 +2,6 @@ import React, { useEffect } from 'react' import { IPageProps } from '../Page' import LayoutHeader from '../LayoutHeader' -import HamburgerMenu from '../HamburgerMenu' import LayoutFooter from '../LayoutFooter' import { handleFirstTab } from '../../utils/front/accessibility' @@ -52,7 +51,6 @@ const MainLayout: LayoutComponent = ({ return ( <> -
{children}
diff --git a/src/components/Page/base.css b/src/components/Page/base.css index b76e5b878a..da3a28cbf6 100644 --- a/src/components/Page/base.css +++ b/src/components/Page/base.css @@ -23,6 +23,7 @@ --layout-width-wide: 1200px; --layout-header-height: 98px; --layout-header-height-collapsed: 78px; + --layout-alert-height: 35px; } html { diff --git a/src/utils/front/hamburgerMenu.ts b/src/utils/front/hamburgerMenu.ts new file mode 100644 index 0000000000..c8dcd0bb8a --- /dev/null +++ b/src/utils/front/hamburgerMenu.ts @@ -0,0 +1,52 @@ +import { + useEffect, + useState, + useCallback, + MouseEvent, + KeyboardEvent +} from 'react' +import styles from '../../components/HamburgerMenu/styles.module.css' +import { logEvent } from '../../utils/front/ga' + +export type HamburgerHelpers = { + opened: boolean + setOpened: (newState: boolean) => void + handleToggle: () => void + handleKeyDown: (e: KeyboardEvent) => void + handleClose: () => void + handleItemClick: (name: string) => (e: MouseEvent) => void +} + +export const useHamburgerMenu: () => HamburgerHelpers = () => { + const [opened, setOpened] = useState(false) + + const handleToggle = useCallback(() => setOpened(!opened), [opened]) + const handleKeyDown = useCallback(e => { + if (e.which === 13) { + handleToggle() + } + }, []) + + const handleClose = useCallback(() => setOpened(false), [opened]) + const handleItemClick = useCallback( + item => (): void => { + close() + logEvent('hamburger', item) + }, + [] + ) + + useEffect(() => { + const method = opened ? 'add' : 'remove' + document.body.classList[method](styles.hiddenScrollbar) + }, [opened]) + + return { + opened, + setOpened, + handleToggle, + handleKeyDown, + handleClose, + handleItemClick + } +} From c58c48428a03fca9d772f089c3f341a8c6948e67 Mon Sep 17 00:00:00 2001 From: rogermparent Date: Tue, 21 Jul 2020 16:12:56 -0400 Subject: [PATCH 2/2] Co-locate useHamburgerMenu with HamburgerMenu components --- src/components/HamburgerMenu/index.tsx | 53 +++++++++++++++++++++++++- src/components/LayoutHeader/index.tsx | 8 ++-- src/utils/front/hamburgerMenu.ts | 52 ------------------------- 3 files changed, 56 insertions(+), 57 deletions(-) delete mode 100644 src/utils/front/hamburgerMenu.ts diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx index a92387ef7b..3557cb15a8 100644 --- a/src/components/HamburgerMenu/index.tsx +++ b/src/components/HamburgerMenu/index.tsx @@ -1,9 +1,15 @@ import cn from 'classnames' -import React, { MouseEvent, KeyboardEvent } from 'react' +import React, { + useEffect, + useState, + useCallback, + MouseEvent, + KeyboardEvent +} from 'react' import HamburgerIcon from '../HamburgerIcon' -import { HamburgerHelpers } from '../../utils/front/hamburgerMenu' import Link from '../Link' +import { logEvent } from '../../utils/front/ga' import { getFirstPage } from '../../utils/shared/sidebar' import { ReactComponent as LogoSVG } from '../../../static/img/logo-white.svg' @@ -14,6 +20,49 @@ import styles from './styles.module.css' const docsPage = getFirstPage() +export type HamburgerHelpers = { + opened: boolean + setOpened: (newState: boolean) => void + handleToggle: () => void + handleKeyDown: (e: KeyboardEvent) => void + handleClose: () => void + handleItemClick: (name: string) => (e: MouseEvent) => void +} + +export const useHamburgerMenu: () => HamburgerHelpers = () => { + const [opened, setOpened] = useState(false) + + const handleToggle = useCallback(() => setOpened(!opened), [opened]) + const handleKeyDown = useCallback(e => { + if (e.which === 13) { + handleToggle() + } + }, []) + + const handleClose = useCallback(() => setOpened(false), [opened]) + const handleItemClick = useCallback( + item => (): void => { + close() + logEvent('hamburger', item) + }, + [] + ) + + useEffect(() => { + const method = opened ? 'add' : 'remove' + document.body.classList[method](styles.hiddenScrollbar) + }, [opened]) + + return { + opened, + setOpened, + handleToggle, + handleKeyDown, + handleClose, + handleItemClick + } +} + export const HamburgerMenu: React.FC< Pick< HamburgerHelpers, diff --git a/src/components/LayoutHeader/index.tsx b/src/components/LayoutHeader/index.tsx index 5a18d1701b..92d00ffd8b 100644 --- a/src/components/LayoutHeader/index.tsx +++ b/src/components/LayoutHeader/index.tsx @@ -6,14 +6,16 @@ import { LayoutModifiers, ILayoutModifiable } from '../MainLayout' import LayoutWidthContainer from '../LayoutWidthContainer' import Link from '../Link' import Nav from './Nav' -import { HamburgerMenu, HamburgerButton } from '../HamburgerMenu' +import { + HamburgerMenu, + HamburgerButton, + useHamburgerMenu +} from '../HamburgerMenu' import { useHeaderIsScrolled } from '../../utils/front/scroll' import { ReactComponent as LogoSVG } from '../../../static/img/logo.svg' import styles from './styles.module.css' -import { useHamburgerMenu } from '../../utils/front/hamburgerMenu' - import LayoutAlert from './alert' const LayoutHeader: React.FC> = ({ modifiers }) => { diff --git a/src/utils/front/hamburgerMenu.ts b/src/utils/front/hamburgerMenu.ts deleted file mode 100644 index c8dcd0bb8a..0000000000 --- a/src/utils/front/hamburgerMenu.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - useEffect, - useState, - useCallback, - MouseEvent, - KeyboardEvent -} from 'react' -import styles from '../../components/HamburgerMenu/styles.module.css' -import { logEvent } from '../../utils/front/ga' - -export type HamburgerHelpers = { - opened: boolean - setOpened: (newState: boolean) => void - handleToggle: () => void - handleKeyDown: (e: KeyboardEvent) => void - handleClose: () => void - handleItemClick: (name: string) => (e: MouseEvent) => void -} - -export const useHamburgerMenu: () => HamburgerHelpers = () => { - const [opened, setOpened] = useState(false) - - const handleToggle = useCallback(() => setOpened(!opened), [opened]) - const handleKeyDown = useCallback(e => { - if (e.which === 13) { - handleToggle() - } - }, []) - - const handleClose = useCallback(() => setOpened(false), [opened]) - const handleItemClick = useCallback( - item => (): void => { - close() - logEvent('hamburger', item) - }, - [] - ) - - useEffect(() => { - const method = opened ? 'add' : 'remove' - document.body.classList[method](styles.hiddenScrollbar) - }, [opened]) - - return { - opened, - setOpened, - handleToggle, - handleKeyDown, - handleClose, - handleItemClick - } -}