diff --git a/src/components/Footer/Address/Address.stories.tsx b/src/components/Footer/Address/Address.stories.tsx index 19ad7d385a..d9cde1ae50 100644 --- a/src/components/Footer/Address/Address.stories.tsx +++ b/src/components/Footer/Address/Address.stories.tsx @@ -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 `, diff --git a/src/components/Footer/Footer.stories.tsx b/src/components/Footer/Footer.stories.tsx index c77187f5fe..219c172c01 100644 --- a/src/components/Footer/Footer.stories.tsx +++ b/src/components/Footer/Footer.stories.tsx @@ -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 @@ -19,6 +20,10 @@ export default { }, } +const mockSubmit = (): void => { + /* mock submit fn */ +} + const returnToTop = (
+ } + secondary={ +
+ + } + heading={

Name of Agency

} + /> +
+ + Facebook + , + + Twitter + , + + YouTube + , + + RSS + , + ]} + /> +

Agency Contact Center

+
+ (800) CALL-GOVT + , + + info@agency.gov + , + ]} + /> +
+
+ } + /> +) diff --git a/src/components/Footer/FooterNav/FooterNav.stories.tsx b/src/components/Footer/FooterNav/FooterNav.stories.tsx index 87f73408a3..d8861d64f2 100644 --- a/src/components/Footer/FooterNav/FooterNav.stories.tsx +++ b/src/components/Footer/FooterNav/FooterNav.stories.tsx @@ -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 `, @@ -35,3 +35,44 @@ export const MediumFooterNav = (): React.ReactElement => ( )} /> ) + +export const BigFooterNav = (): React.ReactElement => ( + + Topic + , + ...Array(3).fill( + + Secondary link + + ), + ], + [ +

+ Topic +

, + + Secondary link that is pretty long + , + ...Array(2).fill( + + Secondary link + + ), + ], + [ +

+ Topic +

, + ...Array(3).fill( + + Secondary link + + ), + ], + ]} + /> +) diff --git a/src/components/Footer/FooterNav/FooterNav.test.tsx b/src/components/Footer/FooterNav/FooterNav.test.tsx index fb671f1f1a..1d5d64f438 100644 --- a/src/components/Footer/FooterNav/FooterNav.test.tsx +++ b/src/components/Footer/FooterNav/FooterNav.test.tsx @@ -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' @@ -9,6 +10,26 @@ const links = Array(4).fill( Primary Link ) + +const extendedLinks = [ + [ + 'Types of Cats', + ...Array(2).fill( + + Cheetah + + ), + ], + [ + 'Musical Gifts', + ...Array(3).fill( + + Purple Rain + + ), + ], +] + describe('FooterNav component', () => { it('renders without errors', () => { const { getByRole } = render() @@ -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() + 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( + + ) + 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() + expect(container.querySelectorAll('a').length).toBe(0) + }) }) diff --git a/src/components/Footer/FooterNav/FooterNav.tsx b/src/components/Footer/FooterNav/FooterNav.tsx index 97f380b332..4997bf1b73 100644 --- a/src/components/Footer/FooterNav/FooterNav.tsx +++ b/src/components/Footer/FooterNav/FooterNav.tsx @@ -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 ): 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 ( - + + ) +} + +const ExtendedNav = ({ + nestedLinks, +}: { + nestedLinks: ExtendedNavLinks +}): React.ReactElement => { + return ( +
+ {nestedLinks.map((links, i) => ( +
+
+
+ ))} +
) } diff --git a/src/components/Footer/Logo/Logo.stories.tsx b/src/components/Footer/Logo/Logo.stories.tsx index d07d818c45..220caaa86c 100644 --- a/src/components/Footer/Logo/Logo.stories.tsx +++ b/src/components/Footer/Logo/Logo.stories.tsx @@ -3,10 +3,10 @@ import React from 'react' import { Logo } from './Logo' export default { - title: 'Logo', + title: 'Footer/Logo', parameters: { info: ` - Used within USWDS 2.0 Footer component + Display logo image with optional heading. Used in USWDS 2.0 Footer component. Source: https://designsystem.digital.gov/components/form-controls/#footer `, diff --git a/src/components/Footer/SignUpForm/SignUpForm.stories.tsx b/src/components/Footer/SignUpForm/SignUpForm.stories.tsx new file mode 100644 index 0000000000..9a6b1b1c23 --- /dev/null +++ b/src/components/Footer/SignUpForm/SignUpForm.stories.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { SignUpForm } from './SignUpForm' + +export default { + title: 'Footer/SignUpForm', + parameters: { + info: ` + Form with a single input and submit button. Used in USWDS 2.0 Footer component. + + Source: https://designsystem.digital.gov/components/form-controls/#footer + `, + }, +} + +const mockSubmit = (): void => { + /* mock submit fn */ +} +export const Example = (): React.ReactElement => ( + +) diff --git a/src/components/Footer/SignUpForm/SignUpForm.test.tsx b/src/components/Footer/SignUpForm/SignUpForm.test.tsx new file mode 100644 index 0000000000..ace927a8ed --- /dev/null +++ b/src/components/Footer/SignUpForm/SignUpForm.test.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { render, fireEvent } from '@testing-library/react' + +import { SignUpForm } from './SignUpForm' + +describe('SignUpForm component', () => { + const emptySubmit = (): void => { + /* mock submit fn */ + } + + it('renders without errors', () => { + const { queryByTestId } = render( + + ) + + expect(queryByTestId('form')).toBeInTheDocument() + }) + + it('renders expected form elements', () => { + const { getByRole } = render( + + ) + expect(getByRole('heading')).toBeInTheDocument() + expect(getByRole('button')).toBeInTheDocument() + expect(getByRole('textbox')).toBeInTheDocument() + }) + + it('renders email input by default', () => { + const { container } = render( + + ) + + const input = container.querySelector('input') + expect(input && input.type).toBe('email') + }) + + it('renders input with type prop', () => { + const { container } = render( + + ) + + const input = container.querySelector('input') + expect(input && input.type).toBe('number') + }) + + it('implements an onSubmit handler', () => { + const mockSubmit = jest.fn() + const { getByTestId } = render( + + ) + + fireEvent.submit(getByTestId('form')) + expect(mockSubmit).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/components/Footer/SignUpForm/SignUpForm.tsx b/src/components/Footer/SignUpForm/SignUpForm.tsx new file mode 100644 index 0000000000..33756cb00a --- /dev/null +++ b/src/components/Footer/SignUpForm/SignUpForm.tsx @@ -0,0 +1,34 @@ +import React from 'react' + +import { Button } from '../../Button/Button' +import { Form } from '../../forms/Form/Form' +import { Label } from '../../forms/Label/Label' +import { TextInput, TextInputProps } from '../../forms/TextInput/TextInput' + +type SignUpFormProps = { + heading: React.ReactNode + label: React.ReactNode + submitButtonText?: React.ReactNode + type?: TextInputProps['type'] + onSubmit: () => void +} + +export const SignUpForm = ({ + heading, + label, + submitButtonText = 'Sign up', + type = 'email', + onSubmit, + ...elementAttributes +}: SignUpFormProps & React.HTMLAttributes): React.ReactElement => { + return ( +
+

{heading}

+
+ + + + +
+ ) +} diff --git a/src/components/Footer/SocialLinks/SocialLinks.stories.tsx b/src/components/Footer/SocialLinks/SocialLinks.stories.tsx index ac27efd544..ff146f55c9 100644 --- a/src/components/Footer/SocialLinks/SocialLinks.stories.tsx +++ b/src/components/Footer/SocialLinks/SocialLinks.stories.tsx @@ -2,6 +2,17 @@ import React from 'react' import { SocialLinks } from './SocialLinks' +export default { + title: 'Footer/SocialLinks', + parameters: { + info: ` + Display social links in styled row. Used in USWDS 2.0 Footer component. + + Source: https://designsystem.digital.gov/components/form-controls/#footer + `, + }, +} + const links = [