Skip to content

Commit

Permalink
feat: New Component SiteAlert (#1099)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirenaBorracha authored and brandonlenz committed May 12, 2021
1 parent 492e8fe commit 0e6e52c
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 0 deletions.
149 changes: 149 additions & 0 deletions src/components/SiteAlert/SiteAlert.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* eslint-disable jsx-a11y/anchor-is-valid */

import React from 'react'

import { SiteAlert } from './SiteAlert'
import { Link } from '../Link/Link'

export default {
title: 'Components/SiteAlert',
component: SiteAlert,
parameters: {
docs: {
description: {
component: `
### USWDS 2.0 SiteAlert component
Source: http://designsystem.digital.gov/components/site-alert
`,
},
},
},
argTypes: {
slim: {
control: {
type: 'boolean',
},
},
showIcon: {
control: {
type: 'boolean',
},
},
variant: {
control: {
type: 'select',
options: ['info', 'emergency'],
},
defaultValue: 'info',
},
},
}

const infoHeading = 'Short alert message'

const additionalContext = (
<p className="usa-alert__text">
Additional context and followup information including{' '}
<Link className="usa-link" href="#">
a link
</Link>
.
</p>
)

const emergencyHeading = 'Emergency alert message'

const infoWithList = (
<ul className="usa-list">
<li>
The primary informational message and{` `}
<Link href="#">a link</Link>
{` `}for supporting context.
</li>
<li>
Another message,{` `}
<Link href="#">and another link</Link>.
</li>
<li>A final informational message.</li>
</ul>
)

const emergencyWithList = (
<ul className="usa-list">
<li>
The primary emergency message and{` `}
<Link href="#">a link</Link>
{` `}for supporting context.
</li>
<li>
Another message,{` `}
<Link href="#">and another link</Link>.
</li>
<li>A final emergency message.</li>
</ul>
)

const shortAlertContent = (
<p className="usa-alert__text">
<strong>Short alert message.</strong>
&nbsp;Additional context and followup information including&nbsp;
<Link href="#">a link</Link>.
</p>
)

export const standardInformationalSiteAlert = (): React.ReactElement => (
<SiteAlert variant="info" heading={infoHeading}>
{additionalContext}
</SiteAlert>
)

export const standardEmergencySiteAlert = (): React.ReactElement => (
<SiteAlert variant="emergency" heading={emergencyHeading}>
{additionalContext}
</SiteAlert>
)

export const informationalAlertWithNoHeader = (): React.ReactElement => (
<SiteAlert variant="info">{shortAlertContent}</SiteAlert>
)

export const emergencyAlertWithNoHeader = (): React.ReactElement => (
<SiteAlert variant="emergency">{shortAlertContent}</SiteAlert>
)

export const informationalAlertWithList = (): React.ReactElement => (
<SiteAlert variant="info" heading={infoHeading}>
{infoWithList}
</SiteAlert>
)

export const emergencyAlertWithList = (): React.ReactElement => (
<SiteAlert
variant="emergency"
heading={emergencyHeading}
aria-label="Site alert">
{emergencyWithList}
</SiteAlert>
)

export const slimEmergencyAlert = (): React.ReactElement => (
<SiteAlert slim variant="emergency">
{shortAlertContent}
</SiteAlert>
)

export const emergencyAlertNoIcon = (): React.ReactElement => (
<SiteAlert showIcon={false} variant="emergency">
{shortAlertContent}
</SiteAlert>
)

export const alertWithCustomControls = (argTypes): React.ReactElement => (
<SiteAlert
slim={argTypes.slim}
showIcon={argTypes.showIcon}
variant={argTypes.variant}>
{shortAlertContent}
</SiteAlert>
)
124 changes: 124 additions & 0 deletions src/components/SiteAlert/SiteAlert.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* eslint-disable jsx-a11y/anchor-is-valid, react/jsx-key */
import React from 'react'
import { render } from '@testing-library/react'

import { Link } from '../Link/Link'

import { SiteAlert } from './SiteAlert'

const testChildren = (
<p className="usa-alert__text">
some default text&nbsp;
<Link href="#">with a link</Link>.
</p>
)

const testDefaultProps = {
heading: 'test heading',
children: testChildren,
}

const testChildrenWithList = (
<ul>
<li>
some default text&nbsp;
<Link href="#">with a link&nbsp;</Link>
</li>
<li>
another list item&nbsp;
<Link href="#">with a link</Link>
</li>
<li>another list item, no link</li>
</ul>
)

describe('SiteAlert component', () => {
it('renders without errors', () => {
const { getByTestId, getByRole } = render(
<SiteAlert variant="info" {...testDefaultProps} />
)

expect(getByTestId('siteAlert')).toBeInTheDocument()
expect(getByRole('link')).toBeInTheDocument()
})

it('accepts a className', () => {
const { getByTestId } = render(
<SiteAlert
variant="info"
className="custom-class-name"
{...testDefaultProps}
/>
)

expect(getByTestId('siteAlert')).toHaveClass(
'usa-site-alert usa-site-alert--info custom-class-name'
)
})

it('accepts a custom aria-label', () => {
const { getByTestId } = render(
<SiteAlert
variant="emergency"
aria-label="custom aria label"
{...testDefaultProps}
/>
)

expect(getByTestId('siteAlert')).toHaveAttribute(
'aria-label',
'custom aria label'
)
})

it('renders a passed in heading', () => {
const { getByRole, queryByText } = render(
<SiteAlert variant="info" {...testDefaultProps} />
)

const heading = getByRole('heading')
expect(heading).toBeInTheDocument()
expect(heading).toHaveClass('usa-alert__heading')
expect(queryByText('test heading')).toBeInTheDocument()
})

it('renders emergency site alert without errors', () => {
const { getByTestId } = render(
<SiteAlert variant="emergency" {...testDefaultProps} />
)

expect(getByTestId('siteAlert')).toHaveClass(
'usa-site-alert usa-site-alert--emergency'
)
})

it('renders passed in link', () => {
const { getByRole } = render(
<SiteAlert variant="info">{testChildren}</SiteAlert>
)

expect(getByRole('link')).toBeInTheDocument()
expect(getByRole('link')).toHaveClass('usa-link')
})

it('renders a passed in list', () => {
const { getAllByRole } = render(
<SiteAlert variant="emergency">{testChildrenWithList}</SiteAlert>
)
expect(getAllByRole('link')).toHaveLength(2)
})

it('renders slim and no icon when passed both, and does not apply no-header class', () => {
const { getByTestId } = render(
<SiteAlert variant="info" slim={true} showIcon={false}>
{testChildren}
</SiteAlert>
)
expect(getByTestId('siteAlert')).toHaveClass(
'usa-site-alert usa-site-alert--info usa-site-alert--no-icon usa-site-alert--slim'
)
expect(getByTestId('siteAlert')).not.toHaveClass(
'usa-site-alert--no-heading'
)
})
})
49 changes: 49 additions & 0 deletions src/components/SiteAlert/SiteAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'
import classnames from 'classnames'

interface SiteAlertProps {
variant: 'info' | 'emergency'
children: React.ReactNode
heading?: string
showIcon?: boolean
slim?: boolean
className?: string
}

export const SiteAlert = ({
variant,
children,
heading,
showIcon = true,
slim = false,
className,
...sectionProps
}: SiteAlertProps & JSX.IntrinsicElements['section']): React.ReactElement => {
const classes = classnames(
'usa-site-alert',
{
'usa-site-alert--info': variant === 'info',
'usa-site-alert--emergency': variant === 'emergency',
'usa-site-alert--no-heading': heading === undefined && !slim,
'usa-site-alert--no-icon': !showIcon,
'usa-site-alert--slim': slim,
},
className
)
return (
<section
data-testid="siteAlert"
className={classes}
aria-label="Site alert"
{...sectionProps}>
<div className="usa-alert">
<div className="usa-alert__body">
{heading && <h3 className="usa-alert__heading">{heading}</h3>}
{children}
</div>
</div>
</section>
)
}

export default SiteAlert
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ export { StepIndicator } from './components/stepindicator/StepIndicator/StepIndi
export { StepIndicatorStep } from './components/stepindicator/StepIndicatorStep/StepIndicatorStep'

export { Search } from './components/Search/Search'
export { SiteAlert } from './components/SiteAlert/SiteAlert'

0 comments on commit 0e6e52c

Please sign in to comment.