Skip to content

Commit

Permalink
feat: Big footer (#142)
Browse files Browse the repository at this point in the history
* Add extended nav behavior inside FooterNav
* Add SignupForm
* Organize stories in subdirectories and add info
  • Loading branch information
haworku committed May 11, 2020
1 parent edbbf13 commit a928773
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/components/Footer/Address/Address.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React from 'react'
import { Address } from './Address'

export default {
title: 'Address',
title: 'Footer/Address',
parameters: {
info: `
Used within USWDS 2.0 Footer component
Display address items (most likely links or simple text) in a row, wrapped in address tag. Used in USWDS 2.0 Footer component.
Source: https://designsystem.digital.gov/components/form-controls/#footer
`,
Expand Down
140 changes: 139 additions & 1 deletion src/components/Footer/Footer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { Button } from '../Button/Button'
import { Footer } from './Footer'
import { FooterNav } from './FooterNav/FooterNav'
import { Logo } from './Logo/Logo'
import { SignUpForm } from './SignUpForm/SignUpForm'
import { SocialLinks } from './SocialLinks/SocialLinks'

export default {
title: 'Footer',
title: 'Footer/Footer',
parameters: {
info: `
USWDS 2.0 Footer component
Expand All @@ -19,6 +20,10 @@ export default {
},
}

const mockSubmit = (): void => {
/* mock submit fn */
}

const returnToTop = (
<div className="grid-container usa-footer__return-to-top">
<Button type="button" unstyled>
Expand Down Expand Up @@ -147,3 +152,136 @@ export const MediumFooter = (): React.ReactElement => (
}
/>
)

export const BigFooter = (): React.ReactElement => (
<Footer
big
returnToTop={returnToTop}
primary={
<div className="grid-container">
<div className="grid-row grid-gap">
<div className="tablet:grid-col-8">
<FooterNav
big
links={[
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
...Array(2).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
<a key="4" className="usa-footer__secondary-link" href="#">
Secondary link that is a bit longer than most of the others
</a>,
<a key="5" className="usa-footer__secondary-link" href="#">
Secondary link
</a>,
],
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
<a key="2" className="usa-footer__secondary-link" href="#">
Secondary link that is pretty long
</a>,
...Array(3).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
...Array(4).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
...Array(4).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
]}
/>
</div>
<div className="tablet:grid-col-4">
<SignUpForm
heading="Sign up"
label="Your email address"
onSubmit={mockSubmit}
/>
</div>
</div>
</div>
}
secondary={
<div className="grid-row grid-gap">
<Logo
big
image={
<img
className="usa-footer__logo-img"
src="/logo-img.png"
alt="img alt text"
/>
}
heading={<h3 className="usa-footer__logo-heading">Name of Agency</h3>}
/>
<div className="usa-footer__contact-links mobile-lg:grid-col-6">
<SocialLinks
links={[
<a
key="facebook"
className="usa-social-link usa-social-link--facebook"
href="#">
<span>Facebook</span>
</a>,
<a
key="twitter"
className="usa-social-link usa-social-link--twitter"
href="#">
<span>Twitter</span>
</a>,
<a
key="youtube"
className="usa-social-link usa-social-link--youtube"
href="#">
<span>YouTube</span>
</a>,
<a
key="rss"
className="usa-social-link usa-social-link--rss"
href="#">
<span>RSS</span>
</a>,
]}
/>
<h3 className="usa-footer__contact-heading">Agency Contact Center</h3>
<Address
big
items={[
<a key="telephone" href="tel:1-800-555-5555">
(800) CALL-GOVT
</a>,
<a key="email" href="mailto:info@agency.gov">
info@agency.gov
</a>,
]}
/>
</div>
</div>
}
/>
)
45 changes: 43 additions & 2 deletions src/components/Footer/FooterNav/FooterNav.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import React from 'react'
import { FooterNav } from './FooterNav'

export default {
title: 'FooterNav',
title: 'Footer/FooterNav',
parameters: {
info: `
Used in USWDS 2.0 Footer component
Display single list of nav items, or grouped nav items in an extended nav. Used in USWDS 2.0 Footer component.
Source: https://designsystem.digital.gov/components/form-controls/#footer
`,
Expand Down Expand Up @@ -35,3 +35,44 @@ export const MediumFooterNav = (): React.ReactElement => (
)}
/>
)

export const BigFooterNav = (): React.ReactElement => (
<FooterNav
big
links={[
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
...Array(3).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
<a key="2" className="usa-footer__secondary-link" href="#">
Secondary link that is pretty long
</a>,
...Array(2).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
[
<h4 key="1" className="usa-footer__primary-link">
Topic
</h4>,
...Array(3).fill(
<a className="usa-footer__secondary-link" href="#">
Secondary link
</a>
),
],
]}
/>
)
43 changes: 42 additions & 1 deletion src/components/Footer/FooterNav/FooterNav.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable jsx-a11y/anchor-is-valid, react/jsx-key */

import React from 'react'
import { render } from '@testing-library/react'

Expand All @@ -9,6 +10,26 @@ const links = Array(4).fill(
Primary Link
</a>
)

const extendedLinks = [
[
'Types of Cats',
...Array(2).fill(
<a className="usa-footer__secondary-link" href="#">
Cheetah
</a>
),
],
[
'Musical Gifts',
...Array(3).fill(
<a className="usa-footer__secondary-link" href="#">
Purple Rain
</a>
),
],
]

describe('FooterNav component', () => {
it('renders without errors', () => {
const { getByRole } = render(<FooterNav links={links} />)
Expand All @@ -20,4 +41,24 @@ describe('FooterNav component', () => {
expect(container.querySelectorAll('a').length).toBe(4)
expect(getAllByText('Primary Link').length).toBe(4)
})

it('renders links with "big" prop', () => {
const { container, getAllByText } = render(<FooterNav links={links} big />)
expect(container.querySelectorAll('a').length).toBe(4)
expect(getAllByText('Primary Link').length).toBe(4)
})

it('renders extended links with "big" prop', () => {
const { container, getAllByText } = render(
<FooterNav links={extendedLinks} big />
)
expect(container.querySelectorAll('a').length).toBe(5)
expect(getAllByText('Purple Rain').length).toBe(3)
expect(getAllByText('Cheetah').length).toBe(2)
})

it('does not render extended nav links without "big" prop', () => {
const { container } = render(<FooterNav links={extendedLinks} />)
expect(container.querySelectorAll('a').length).toBe(0)
})
})
77 changes: 68 additions & 9 deletions src/components/Footer/FooterNav/FooterNav.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,95 @@
import React from 'react'
import classnames from 'classnames'

type ExtendedNavLinks = [React.ReactNode[]]

type FooterNavProps = {
big?: boolean
medium?: boolean
slim?: boolean
links: React.ReactNode[]
/*
Union type. Array of navigation links or multidimensional array of ExtendedNavLinks.
ExtendedNavLinks are ordered sub arrays that will be displayed as columns, with the first element used as the section heading.
ExtendedNavLinks can only be used with "big" prop size
*/
links: React.ReactNode[] | ExtendedNavLinks
}

function isExtendedNavLinks(
links: React.ReactNode[] | ExtendedNavLinks
): links is ExtendedNavLinks {
return (links as ExtendedNavLinks)[0].constructor === Array
}

export const FooterNav = (
props: FooterNavProps & React.HTMLAttributes<HTMLElement>
): React.ReactElement => {
const { medium, slim, links, ...elementAttributes } = props
const { big, medium, slim, links, ...elementAttributes } = props

const navClasses = classnames(`usa-footer__nav`, elementAttributes.className)
const listItemClasses = classnames(
'desktop:grid-col-auto usa-footer__primary-content',
{
'mobile-lg:grid-col-4': medium,
'mobile-lg:grid-col-4': big || medium,
'mobile-lg:grid-col-6': slim,
}
)

return (
<nav className={navClasses} aria-label="Footer navigation">
<ul className="grid-row grid-gap">
{links.map((link, i) => (
<li key={`navLink-${i}`} className={listItemClasses}>
<nav
{...elementAttributes}
className="usa-footer__nav"
aria-label="Footer navigation">
{big && isExtendedNavLinks(links) && <ExtendedNav nestedLinks={links} />}

{!isExtendedNavLinks(links) && (
<ul className="grid-row grid-gap">
{links.map((link, i) => (
<li key={`navLink-${i}`} className={listItemClasses}>
{link}
</li>
))}
</ul>
)}
</nav>
)
}

const Section = ({
links,
}: {
links: React.ReactNode[]
}): React.ReactElement => {
const primaryLinkOrHeading = links[0]
const secondaryLinks = links.slice(1)

return (
<section className="usa-footer__primary-content usa-footer__primary-content--collapsible">
<h4 className="usa-footer__primary-link">{primaryLinkOrHeading}</h4>
<ul className="usa-list usa-list--unstyled">
{secondaryLinks.map((link, i) => (
<li key={`navLink-${i}`} className="usa-footer__secondary-link">
{link}
</li>
))}
</ul>
</nav>
</section>
)
}

const ExtendedNav = ({
nestedLinks,
}: {
nestedLinks: ExtendedNavLinks
}): React.ReactElement => {
return (
<div className="grid-row grid-gap-4">
{nestedLinks.map((links, i) => (
<div
key={`linkSection-${i}`}
className="mobile-lg:grid-col-6 desktop:grid-col-3">
<Section links={links} />
</div>
))}
</div>
)
}
Loading

0 comments on commit a928773

Please sign in to comment.