Skip to content

Commit

Permalink
feat: navbar V2 (#124)
Browse files Browse the repository at this point in the history
* feat: swap old nav with MUI nav

* refact: clean up

* add tooltip

* add tooltip to ScrollTop

* fix test

* test: add test coverage

Co-authored-by: wtlau <[email protected]>
  • Loading branch information
wtLau and wtLau authored Oct 22, 2022
1 parent ef107df commit 7cad1cb
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 251 deletions.
28 changes: 0 additions & 28 deletions components/common/Navbar/NavBar.module.css

This file was deleted.

25 changes: 16 additions & 9 deletions components/common/Navbar/NavBar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { render, screen } from '@testing-library/react'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'

import NavBar from './NavBar'

describe('<NavBar />', () => {
it('should render default', () => {
render(<NavBar />)

Object.defineProperty(window, 'innerHeight', {
writable: true,
configurable: true,
value: 150,
})

window.dispatchEvent(new Event('resize'))
expect(screen.getByRole('link', { name: 'Brian Lau' })).toHaveAttribute(
'href',
'/'
)
})
it('Mobile - Drawer menu', () => {
render(
<div style={{ width: '200px' }}>
<NavBar />
</div>
)

expect(screen.getByRole('link', { name: 'Brian Lau' })).toBeInTheDocument()
const menuBtn = screen.getByRole('button', { name: 'open drawer' })
fireEvent.click(menuBtn)
expect(screen.getByRole('link', { name: 'Blog' })).toHaveAttribute(
'href',
'/blog'
)
})
})
291 changes: 192 additions & 99 deletions components/common/Navbar/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,118 +1,211 @@
import * as React from 'react'
import AppBar from '@mui/material/AppBar'
import Toolbar from '@mui/material/Toolbar'
import Typography from '@mui/material/Typography'
import useScrollTrigger from '@mui/material/useScrollTrigger'
import Box from '@mui/material/Box'
import Fab from '@mui/material/Fab'
import Fade from '@mui/material/Fade'
import {
AppBar,
Toolbar,
Typography,
ButtonBase,
Divider,
Drawer,
Grid,
IconButton,
List,
ListItemText,
Hidden,
ListItem,
ListItemIcon,
ListItemButton,
ButtonBase,
Slide,
Tooltip,
useTheme,
} from '@mui/material'
import { styled } from '@mui/material/styles'
import React from 'react'
import { Link } from '@components/ui'

import SideDrawer from './SideDrawer'
import MenuIcon from '@mui/icons-material/Menu'
import ThemeButton from './ThemeButton'
import { Link } from '@components/ui'
import ScrollTop from './ScrollTop'
const drawerWidth = 240
const navTile = 'Brian Lau'

const PREFIX = 'NavBar'

const classes = {
appBar: `${PREFIX}-appBar`,
toolBar: `${PREFIX}-toolBar`,
logo: `${PREFIX}-logo`,
navbarDisplayFlex: `${PREFIX}-navbarDisplayFlex`,
navDisplayFlex: `${PREFIX}-navDisplayFlex`,
linkText: `${PREFIX}-linkText`,
const navItems = [
{ title: `Blog`, path: `/blog` },
{ title: 'About', path: '/about' },
// {
// title: `Github`,
// path: `https://github.com/wtLau`,
// external: true,
// },
// {
// title: `LinkedIn`,
// path: `https://www.linkedin.com/in/brian-lau/`,
// external: true,
// },
]
interface Props {
children: React.ReactElement
}

// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
const Root = styled('div')(({ theme }) => ({
[`& .${classes.appBar}`]: {
height: '100px',
display: 'flex',
justifyContent: 'center',
flexDirection: 'row',
},

[`& .${classes.toolBar}`]: {
maxWidth: theme.breakpoints.values.lg,
width: '100%',
display: 'flex',
justifyContent: 'space-between',
},
function HideOnScroll({ children }: Props) {
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 100,
})

[`& .${classes.navbarDisplayFlex}`]: {
display: `flex`,
justifyContent: `space-between`,
},
return (
<Slide appear={false} direction='down' in={!trigger}>
{children}
</Slide>
)
}

[`& .${classes.navDisplayFlex}`]: {
display: `flex`,
justifyContent: `space-between`,
},
}))
const NavBar = () => {
const [mobileOpen, setMobileOpen] = React.useState(false)
const theme = useTheme()

const navLinks = [
{ title: `Blog`, path: `/blog` },
{ title: 'About', path: '/about' },
{
title: `Github`,
path: `https://github.com/wtLau`,
external: true,
},
{
title: `LinkedIn`,
path: `https://www.linkedin.com/in/brian-lau/`,
external: true,
},
]
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen)
}

const NavBar = () => {
const drawer = (
<Box
onClick={handleDrawerToggle}
sx={{
textAlign: 'center',
height: '100%',
backgroundImage: 'none',
backgroundColor: theme.palette.background.default,
}}
>
<Typography
variant='h6'
noWrap
component={Link}
href='/'
sx={{
my: 6,
display: { xs: 'flex' },
justifyContent: 'center',
// fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
{navTile}
</Typography>
<Divider />
<List>
{navItems.map((item) => (
<ListItem key={item.title}>
<ButtonBase
LinkComponent={Link}
href={item.path}
sx={{ width: '100%', py: 1 }}
>
{item.title}
</ButtonBase>
</ListItem>
))}
</List>
<Divider />
<ListItemIcon sx={{ py: 1 }}>
<ThemeButton />
</ListItemIcon>
</Box>
)
return (
<Root>
<AppBar className={classes.appBar} color='transparent'>
<Toolbar className={classes.toolBar}>
<Link href='/'>
<ButtonBase focusRipple>
<Typography variant='body1' color='textPrimary' align='center'>
Brian Lau
<>
<HideOnScroll>
<AppBar
sx={{
backgroundImage: 'none',
backgroundColor: theme.palette.background.default,
py: 2,
}}
>
<Toolbar
sx={{
maxWidth: theme.breakpoints.values.lg,
alignSelf: 'center',
width: '100%',
}}
>
<Box sx={{ flexGrow: 1, display: { sm: 'block' } }}>
<Typography
variant='h6'
noWrap
component={Link}
href='/'
sx={{
mr: 2,
// fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
{navTile}
</Typography>
</ButtonBase>
</Link>

<Hidden mdDown>
<List
component='nav'
aria-labelledby='main navigation'
className={classes.navDisplayFlex}
>
<Link href='/about' color='textPrimary'>
<ListItemButton>
<ListItemText primary={'About'} />
</ListItemButton>
</Link>

<Link href='/blog' color='textPrimary'>
<ListItemButton>
<ListItemText primary={'Blog'} />
</ListItemButton>
</Link>
</Box>
<Box>
<IconButton
aria-label='open drawer'
edge='end'
onClick={handleDrawerToggle}
sx={{ display: { sm: 'none' } }}
>
<MenuIcon />
</IconButton>
<Grid
container
direction='row'
spacing={4}
alignItems='center'
sx={{ display: { xs: 'none', sm: 'flex' } }}
>
{navItems.map((item) => (
<Grid item key={item.title}>
<Link href={item.path} sx={{ color: 'inherit' }}>
{item.title}
</Link>
</Grid>
))}

<ListItemIcon>
<ThemeButton />
</ListItemIcon>
</List>
</Hidden>
<Grid item>
<ListItemIcon>
<ThemeButton />
</ListItemIcon>
</Grid>
</Grid>
</Box>
</Toolbar>
</AppBar>
</HideOnScroll>
<Box component='nav'>
<Drawer
variant='temporary'
anchor='right'
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: 'flex', sm: 'none' },
'& .MuiDrawer-paper': {
boxSizing: 'border-box',
width: drawerWidth,
},
}}
>
{drawer}
</Drawer>
</Box>
<Toolbar id='back-to-top-anchor' />

<Hidden mdUp>
<SideDrawer navLinks={navLinks} />
</Hidden>
</Toolbar>
</AppBar>
<Toolbar />
</Root>
<ScrollTop />
</>
)
}
export default NavBar
Loading

0 comments on commit 7cad1cb

Please sign in to comment.