Skip to content

Commit

Permalink
feat(Navbar): refactor navbar for better readbility
Browse files Browse the repository at this point in the history
  • Loading branch information
MellyGray authored and pdurbin committed Apr 18, 2023
1 parent 1e1c54b commit 7ce257a
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 144 deletions.
35 changes: 17 additions & 18 deletions src/sections/layout/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import logo from '../../ui/logo.svg'
import { useTranslation } from 'react-i18next'
import { Navbar } from '../../ui/navbar/Navbar'
import { Route } from '../../route.enum'
import { Link } from '../../ui/navbar/NavbarProps'

type User = {
name: string
Expand All @@ -15,23 +14,23 @@ interface HeaderProps {
export function Header({ user }: HeaderProps) {
const { t } = useTranslation('header')

const links: Link[] = user
? [{ title: user.name, value: [{ title: t('logOut'), value: Route.LOG_OUT }] }]
: [
{ title: t('signUp'), value: Route.SIGN_UP },
{ title: t('logIn'), value: Route.LOG_IN }
]

return (
<>
<Navbar
brand={{
logo: { src: logo, altText: t('brandLogoImage') },
title: t('brandTitle'),
path: Route.HOME
}}
links={links}
/>
</>
<Navbar
brand={{
title: t('brandTitle'),
href: Route.HOME,
logoImgSrc: logo
}}>
{user ? (
<Navbar.Dropdown title={user.name} id="dropdown-user">
<Navbar.Dropdown.Item href={Route.LOG_OUT}>{t('logOut')}</Navbar.Dropdown.Item>
</Navbar.Dropdown>
) : (
<>
<Navbar.Link href={Route.LOG_IN}>{t('logIn')}</Navbar.Link>
<Navbar.Link href={Route.SIGN_UP}>{t('signUp')}</Navbar.Link>
</>
)}
</Navbar>
)
}
39 changes: 22 additions & 17 deletions src/sections/ui/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import { Navbar as NavbarBS } from 'react-bootstrap'
import { Nav } from 'react-bootstrap'
import { Link, NavbarProps } from './NavbarProps'
import { NavDropdown } from './nav-dropdown/NavDropdown'
import { NavbarDropdown } from './navbar-dropdown/NavbarDropdown'
import { Container } from '../grid/Container'
import { PropsWithChildren } from 'react'
import { NavbarLink } from './NavbarLink'

export function Navbar({ brand, links }: NavbarProps) {
interface Brand {
title: string
href: string
logoImgSrc: string
}

export interface NavbarProps {
brand: Brand
}

function Navbar({ brand, children }: PropsWithChildren<NavbarProps>) {
return (
<NavbarBS collapseOnSelect bg="light" expand="lg" fixed="top">
<Container>
<NavbarBS.Brand href={brand.path}>
<img width="28" height="28" src={brand.logo.src} alt={brand.logo.altText} />
<NavbarBS.Brand href={brand.href}>
<img width="28" height="28" src={brand.logoImgSrc} alt="Brand Logo Image" />
{brand.title}
</NavbarBS.Brand>
<NavbarBS.Toggle aria-controls="responsive-navbar-nav" />
<NavbarBS.Collapse id="responsive-navbar-nav">
<Nav>
{links.length != 0 &&
links.map((link: Link, index) =>
typeof link.value == 'string' ? (
<Nav.Link eventKey={index} key={index} href={link.value}>
{link.title}
</Nav.Link>
) : (
<NavDropdown key={index} title={link.title} links={link.value} />
)
)}
</Nav>
<Nav>{children}</Nav>
</NavbarBS.Collapse>
</Container>
</NavbarBS>
)
}

Navbar.Link = NavbarLink
Navbar.Dropdown = NavbarDropdown

export { Navbar }
10 changes: 10 additions & 0 deletions src/sections/ui/navbar/NavbarLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Nav } from 'react-bootstrap'
import { PropsWithChildren } from 'react'

interface NavbarLinkProps {
href: string
}

export function NavbarLink({ href, children }: PropsWithChildren<NavbarLinkProps>) {
return <Nav.Link href={href}>{children}</Nav.Link>
}
20 changes: 0 additions & 20 deletions src/sections/ui/navbar/NavbarProps.ts

This file was deleted.

23 changes: 0 additions & 23 deletions src/sections/ui/navbar/nav-dropdown/NavDropdown.tsx

This file was deleted.

20 changes: 20 additions & 0 deletions src/sections/ui/navbar/navbar-dropdown/NavbarDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NavDropdown as NavDropdownBS } from 'react-bootstrap'
import { PropsWithChildren } from 'react'
import { NavbarDropdownItem } from './NavbarDropdownItem'

interface DropdownProps {
title: string
id: string
}

function NavbarDropdown({ title, id, children }: PropsWithChildren<DropdownProps>) {
return (
<NavDropdownBS title={title} id={id}>
{children}
</NavDropdownBS>
)
}

NavbarDropdown.Item = NavbarDropdownItem

export { NavbarDropdown }
10 changes: 10 additions & 0 deletions src/sections/ui/navbar/navbar-dropdown/NavbarDropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NavDropdown } from 'react-bootstrap'
import { PropsWithChildren } from 'react'

interface NavbarDropdownItemProps {
href: string
}

export function NavbarDropdownItem({ href, children }: PropsWithChildren<NavbarDropdownItemProps>) {
return <NavDropdown.Item href={href}>{children}</NavDropdown.Item>
}
40 changes: 12 additions & 28 deletions src/stories/ui/navbar/Navbar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,14 @@ type Story = StoryObj<typeof Navbar>
export const Default: Story = {
render: () => (
<CanvasFixedHeight height={150}>
<Navbar
brand={{ logo: { src: logo, altText: 'logo' }, title: 'Brand Title', path: '#' }}
links={[
{ title: 'Link 1 ', value: '#' },
{ title: 'Link 2', value: '#' },
{
title: 'Dropdown 1',
value: [
{ title: 'Link 3 ', value: '#' },
{ title: 'Link 4', value: '#' }
]
},
{
title: 'Dropdown 2',
value: [
{ title: 'Link 5 ', value: '#' },
{ title: 'Link 6', value: '#' }
]
}
]}
/>
<Navbar brand={{ title: 'Brand Title', href: '#', logoImgSrc: logo }}>
<Navbar.Link href="/link-1">Link 1</Navbar.Link>
<Navbar.Link href="/link-2">Link 2</Navbar.Link>
<Navbar.Dropdown title="Dropdown 1" id="dropdown">
<Navbar.Dropdown.Item href="/link-3">Link 3</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/link-4">Link 4</Navbar.Dropdown.Item>
</Navbar.Dropdown>
</Navbar>
</CanvasFixedHeight>
)
}
Expand All @@ -68,13 +55,10 @@ export const UseCaseMenu: Story = {
name: 'Example use case: Menu',
render: () => (
<CanvasFixedHeight height={150}>
<Navbar
brand={{ logo: { src: logo, altText: 'logo' }, title: 'Dataverse', path: '#' }}
links={[
{ title: 'Sign Up', value: '#' },
{ title: 'Log In', value: '#' }
]}
/>
<Navbar brand={{ title: 'Dataverse', href: '#', logoImgSrc: logo }}>
<Navbar.Link href="/sing-up">Sing Up</Navbar.Link>
<Navbar.Link href="/login">Log In</Navbar.Link>
</Navbar>
</CanvasFixedHeight>
)
}
4 changes: 2 additions & 2 deletions tests/sections/layout/Layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ describe('Layout', () => {
it('renders the header', () => {
renderWithRouter(<Layout />)

const brandLink = screen.getByRole('link', { name: 'brandLogoImage brandTitle' })
const brandLink = screen.getByRole('link', { name: 'Brand Logo Image brandTitle' })
expect(brandLink).toBeInTheDocument()

const brandImg = screen.getByRole('img', { name: 'brandLogoImage' })
const brandImg = screen.getByRole('img', { name: 'Brand Logo Image' })
expect(brandImg).toBeInTheDocument()

const signUpLink = screen.getByRole('link', { name: 'signUp' })
Expand Down
44 changes: 25 additions & 19 deletions tests/sections/ui/navbar/Navbar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,33 @@ import { render, fireEvent } from '@testing-library/react'
import { Navbar } from '../../../../src/sections/ui/navbar/Navbar'

const brand = {
logo: { src: 'logo.png', altText: 'Logo Alt Text' },
logoImgSrc: '/logo.svg',
title: 'Brand Title',
path: '/home'
href: '/home'
}

const links = [
{ title: 'Link 1', value: '/link1' },
{ title: 'Link 2', value: '/link2' },
{
title: 'Dropdown',
value: [
{ title: 'Sublink 1', value: '/sublink1' },
{ title: 'Sublink 2', value: '/sublink2' }
]
}
]

describe('Navbar component', () => {
test('renders the brand logo and title', () => {
const { getByRole } = render(<Navbar brand={brand} links={[]} />)
const { getByRole } = render(<Navbar brand={brand} />)

const logoImage = getByRole('img', { name: 'Logo Alt Text' })
const logoImage = getByRole('img', { name: 'Brand Logo Image' })
expect(logoImage).toBeInTheDocument()

const brandElement = getByRole('link', { name: 'Logo Alt Text Brand Title' })
const brandElement = getByRole('link', { name: 'Brand Logo Image Brand Title' })
expect(brandElement).toBeInTheDocument()
})

test('renders the navbar links', () => {
const { getByRole } = render(<Navbar brand={brand} links={links} />)
const { getByRole } = render(
<Navbar brand={brand}>
<Navbar.Link href="/link-1">Link 1</Navbar.Link>
<Navbar.Link href="/link-2">Link 2</Navbar.Link>
<Navbar.Dropdown title="Dropdown" id="dropdown">
<Navbar.Dropdown.Item href="/sublink-1">Sublink 1</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/sublink-2">Sublink 2</Navbar.Dropdown.Item>
</Navbar.Dropdown>
</Navbar>
)

const link1Element = getByRole('link', { name: 'Link 1' })
expect(link1Element).toBeInTheDocument()
Expand All @@ -44,7 +41,16 @@ describe('Navbar component', () => {
})

test('shows the sublinks when the dropdown is clicked', async () => {
const { getByRole, findByRole } = render(<Navbar brand={brand} links={links} />)
const { getByRole, findByRole } = render(
<Navbar brand={brand}>
<Navbar.Link href="/link-1">Link 1</Navbar.Link>
<Navbar.Link href="/link-2">Link 2</Navbar.Link>
<Navbar.Dropdown title="Dropdown" id="dropdown">
<Navbar.Dropdown.Item href="/sublink-1">Sublink 1</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/sublink-2">Sublink 2</Navbar.Dropdown.Item>
</Navbar.Dropdown>
</Navbar>
)

const dropdownElement = getByRole('button', { name: 'Dropdown' })

Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { fireEvent, render } from '@testing-library/react'
import { NavDropdown } from '../../../../../src/sections/ui/navbar/nav-dropdown/NavDropdown'

const links = [
{ title: 'Link 1', value: '/link1' },
{ title: 'Link 2', value: '/link2' },
{
title: 'Link 3',
value: [
{ title: 'Sublink 1', value: '/sublink1' },
{ title: 'Sublink 2', value: '/sublink2' }
]
}
]

describe('NavDropdown component', () => {
import { NavbarDropdown } from '../../../../../src/sections/ui/navbar/navbar-dropdown/NavbarDropdown'
import { Navbar } from '../../../../../src/sections/ui/navbar/Navbar'

describe('NavbarDropdown component', () => {
test('renders the dropdown title', () => {
const { getByRole } = render(<NavDropdown title="Dropdown Title" links={links} />)
const { getByRole } = render(
<NavbarDropdown title="Dropdown Title" id="dropdown">
<Navbar.Dropdown.Item href="/link-1">Link 1</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/link-2">Link 2</Navbar.Dropdown.Item>
</NavbarDropdown>
)

const titleElement = getByRole('button', { name: 'Dropdown Title' })
expect(titleElement).toBeInTheDocument()
})

test('renders the dropdown links', async () => {
const { getByRole, findByRole } = render(<NavDropdown title="Dropdown Title" links={links} />)
const { getByRole, findByRole } = render(
<NavbarDropdown title="Dropdown Title" id="dropdown">
<Navbar.Dropdown.Item href="/link-1">Link 1</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/link-2">Link 2</Navbar.Dropdown.Item>
<NavbarDropdown title="Link 3" id="dropdown-2">
<Navbar.Dropdown.Item href="/sublink-1">Sublink 1</Navbar.Dropdown.Item>
<Navbar.Dropdown.Item href="/sublink-2">Sublink 2</Navbar.Dropdown.Item>
</NavbarDropdown>
</NavbarDropdown>
)

const dropdownTitle = getByRole('button', { name: 'Dropdown Title' })

Expand Down

0 comments on commit 7ce257a

Please sign in to comment.