Skip to content

Commit

Permalink
Add medium footer (#126)
Browse files Browse the repository at this point in the history
* Add FooterNav, SocialLinks components 
* Add medium Footer story
* Add medium styles to Logo, Address components
  • Loading branch information
haworku committed May 5, 2020
1 parent 379788f commit 9da3ae4
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 67 deletions.
46 changes: 33 additions & 13 deletions src/components/Footer/Address/Address.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
import React from 'react'

import classnames from 'classnames'
type AddressProps = {
big?: boolean
medium?: boolean
slim?: boolean
/*
Contact info items - e.g. anchor tags or text for email, phone, website, etc.
*/
items: React.ReactNode[]
}

export const Address = ({
big,
medium,
slim,
items,
}: AddressProps & React.HTMLAttributes<HTMLElement>): React.ReactElement => (
<address className="usa-footer__address">
<div className="grid-row grid-gap">
{items.map((item, i) => (
<div
className="grid-col-auto mobile-lg:grid-col-12 desktop:grid-col-auto"
key={`addressItem-${i}`}>
<div className="usa-footer__contact-info">{item}</div>
}: AddressProps & React.HTMLAttributes<HTMLElement>): React.ReactElement => {
const itemClasses = classnames({
'grid-col-auto': big || medium,
'grid-col-auto mobile-lg:grid-col-12 desktop:grid-col-auto': slim,
})
return (
<address className="usa-footer__address">
{slim ? (
<div className="grid-row grid-gap">
{items.map((item, i) => (
<div className={itemClasses} key={`addressItem-${i}`}>
<div className="usa-footer__contact-info">{item}</div>
</div>
))}
</div>
) : (
<div className="usa-footer__contact-info grid-row grid-gap">
{items.map((item, i) => (
<div className={itemClasses} key={`addressItem-${i}`}>
{item}
</div>
))}
</div>
))}
</div>
</address>
)
)}
</address>
)
}
123 changes: 85 additions & 38 deletions src/components/Footer/Footer.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'
import classnames from 'classnames'

import { Address } from './Address/Address'
import { Button } from '../Button/Button'
import { Footer } from './Footer'
import { FooterNav } from './FooterNav/FooterNav'
import { Logo } from './Logo/Logo'
import { SocialLinks } from './SocialLinks/SocialLinks'

export default {
title: 'Footer',
Expand All @@ -18,42 +19,6 @@ export default {
},
}

type SizeProps = {
big?: boolean
medium?: boolean
slim?: boolean
}

// Placeholder until a dynamic Nav is built out and can be used
const MockNav = ({ medium, slim }: SizeProps): React.ReactElement => {
const itemClasses = classnames(
'desktop:grid-col-auto usa-footer__primary-content',
{
'mobile-lg:grid-col-4': medium,
'mobile-lg:grid-col-6': slim,
}
)

const items = Array(4).fill({
href: 'javascript:void(0);',
heading: 'Primary Link',
})

return (
<nav className="usa-footer__nav" aria-label="Footer navigation">
<ul className="grid-row grid-gap">
{items.map((item, i) => (
<li key={`navItem-${i}`} className={itemClasses}>
<a className="usa-footer__primary-link" href={item.href}>
{item.heading}
</a>
</li>
))}
</ul>
</nav>
)
}

const returnToTop = (
<div className="grid-container usa-footer__return-to-top">
<Button type="button" unstyled>
Expand All @@ -69,10 +34,18 @@ export const SlimFooter = (): React.ReactElement => (
primary={
<div className="usa-footer__primary-container grid-row">
<div className="mobile-lg:grid-col-8">
<MockNav slim />
<FooterNav
slim
links={Array(4).fill(
<a className="usa-footer__primary-link" href="#">
Primary Link
</a>
)}
/>
</div>
<div className="tablet:grid-col-4">
<Address
slim
items={[
<a key="telephone" href="tel:1-800-555-5555">
(800) CALL-GOVT
Expand Down Expand Up @@ -100,3 +73,77 @@ export const SlimFooter = (): React.ReactElement => (
}
/>
)

export const MediumFooter = (): React.ReactElement => (
<Footer
medium
returnToTop={returnToTop}
primary={
<FooterNav
medium
links={Array(4).fill(
<a className="usa-footer__primary-link" href="#">
Primary Link
</a>
)}
/>
}
secondary={
<div className="grid-row grid-gap">
<Logo
medium
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
medium
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>
}
/>
)
23 changes: 9 additions & 14 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,15 @@ type FooterProps = {
secondary: React.ReactNode
}

// TODO: Add in "Return to Top" handling
export const Footer = (
props: FooterProps & React.HTMLAttributes<HTMLElement>
): React.ReactElement => {
const {
big,
medium,
slim,
returnToTop,
primary,
secondary,
...footerAttributes
} = props

export const Footer = ({
big,
medium,
slim,
returnToTop,
primary,
secondary,
...footerAttributes
}: FooterProps & React.HTMLAttributes<HTMLElement>): React.ReactElement => {
const classes = classnames(
'usa-footer',
{
Expand Down
37 changes: 37 additions & 0 deletions src/components/Footer/FooterNav/FooterNav.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'

import { FooterNav } from './FooterNav'

export default {
title: 'FooterNav',
parameters: {
info: `
Used in USWDS 2.0 Footer component
Source: https://designsystem.digital.gov/components/form-controls/#footer
`,
},
}

export const SlimFooterNav = (): React.ReactElement => (
<FooterNav
slim
links={Array(4).fill(
<a className="usa-footer__primary-link" href="#">
PrimaryLink
</a>
)}
/>
)

export const MediumFooterNav = (): React.ReactElement => (
<FooterNav
medium
links={Array(4).fill(
<a className="usa-footer__primary-link" href="#">
PrimaryLink
</a>
)}
/>
)
23 changes: 23 additions & 0 deletions src/components/Footer/FooterNav/FooterNav.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'
import { render } from '@testing-library/react'

import { FooterNav } from './FooterNav'

const links = Array(4).fill(
<a className="usa-footer__primary-link" href="#">
Primary Link
</a>
)
describe('FooterNav component', () => {
it('renders without errors', () => {
const { getByRole } = render(<FooterNav links={links} />)
expect(getByRole('navigation')).toBeInTheDocument()
})

it('renders links', () => {
const { container, getAllByText } = render(<FooterNav links={links} />)
expect(container.querySelectorAll('a').length).toBe(4)
expect(getAllByText('Primary Link').length).toBe(4)
})
})
36 changes: 36 additions & 0 deletions src/components/Footer/FooterNav/FooterNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import classnames from 'classnames'

type FooterNavProps = {
big?: boolean
medium?: boolean
slim?: boolean
links: React.ReactNode[]
}

export const FooterNav = (
props: FooterNavProps & React.HTMLAttributes<HTMLElement>
): React.ReactElement => {
const { 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-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}>
{link}
</li>
))}
</ul>
</nav>
)
}
9 changes: 7 additions & 2 deletions src/components/Footer/Logo/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,16 @@ export const Logo = ({
elementAttributes.className
)

const columnClasses = classnames({
'mobile-lg:grid-col-auto': big || medium,
'grid-col-auto': slim,
})

return (
<div className={containerClasses} data-testid="footerLogo">
<>
<div className="grid-col-auto">{image}</div>
{heading && <div className="grid-col-auto">{heading}</div>}
<div className={columnClasses}>{image}</div>
{heading && <div className={columnClasses}>{heading}</div>}
</>
</div>
)
Expand Down
28 changes: 28 additions & 0 deletions src/components/Footer/SocialLinks/SocialLinks.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'
import { SocialLinks } from './SocialLinks'

const 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>,
]
export const Example = (): React.ReactElement => <SocialLinks links={links} />
Loading

0 comments on commit 9da3ae4

Please sign in to comment.