Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add big footer #142

Merged
merged 5 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a heads up, I created a list component that could be used here too. It's not merged in yet though, so 🤷
https://github.com/trussworks/react-uswds/blob/61535c9510d4d528f02b09aaaa45de8ff0ebd789/src/components/header/List/List.stories.tsx

{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