diff --git a/src/components/SiteAlert/SiteAlert.stories.tsx b/src/components/SiteAlert/SiteAlert.stories.tsx new file mode 100644 index 0000000000..83bcff27d5 --- /dev/null +++ b/src/components/SiteAlert/SiteAlert.stories.tsx @@ -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 = ( +

+ Additional context and followup information including{' '} + + a link + + . +

+) + +const emergencyHeading = 'Emergency alert message' + +const infoWithList = ( + +) + +const emergencyWithList = ( + +) + +const shortAlertContent = ( +

+ Short alert message. +  Additional context and followup information including  + a link. +

+) + +export const standardInformationalSiteAlert = (): React.ReactElement => ( + + {additionalContext} + +) + +export const standardEmergencySiteAlert = (): React.ReactElement => ( + + {additionalContext} + +) + +export const informationalAlertWithNoHeader = (): React.ReactElement => ( + {shortAlertContent} +) + +export const emergencyAlertWithNoHeader = (): React.ReactElement => ( + {shortAlertContent} +) + +export const informationalAlertWithList = (): React.ReactElement => ( + + {infoWithList} + +) + +export const emergencyAlertWithList = (): React.ReactElement => ( + + {emergencyWithList} + +) + +export const slimEmergencyAlert = (): React.ReactElement => ( + + {shortAlertContent} + +) + +export const emergencyAlertNoIcon = (): React.ReactElement => ( + + {shortAlertContent} + +) + +export const alertWithCustomControls = (argTypes): React.ReactElement => ( + + {shortAlertContent} + +) diff --git a/src/components/SiteAlert/SiteAlert.test.tsx b/src/components/SiteAlert/SiteAlert.test.tsx new file mode 100644 index 0000000000..4bc9fc2d45 --- /dev/null +++ b/src/components/SiteAlert/SiteAlert.test.tsx @@ -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 = ( +

+ some default text  + with a link. +

+) + +const testDefaultProps = { + heading: 'test heading', + children: testChildren, +} + +const testChildrenWithList = ( + +) + +describe('SiteAlert component', () => { + it('renders without errors', () => { + const { getByTestId, getByRole } = render( + + ) + + expect(getByTestId('siteAlert')).toBeInTheDocument() + expect(getByRole('link')).toBeInTheDocument() + }) + + it('accepts a className', () => { + const { getByTestId } = render( + + ) + + expect(getByTestId('siteAlert')).toHaveClass( + 'usa-site-alert usa-site-alert--info custom-class-name' + ) + }) + + it('accepts a custom aria-label', () => { + const { getByTestId } = render( + + ) + + expect(getByTestId('siteAlert')).toHaveAttribute( + 'aria-label', + 'custom aria label' + ) + }) + + it('renders a passed in heading', () => { + const { getByRole, queryByText } = render( + + ) + + 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( + + ) + + expect(getByTestId('siteAlert')).toHaveClass( + 'usa-site-alert usa-site-alert--emergency' + ) + }) + + it('renders passed in link', () => { + const { getByRole } = render( + {testChildren} + ) + + expect(getByRole('link')).toBeInTheDocument() + expect(getByRole('link')).toHaveClass('usa-link') + }) + + it('renders a passed in list', () => { + const { getAllByRole } = render( + {testChildrenWithList} + ) + expect(getAllByRole('link')).toHaveLength(2) + }) + + it('renders slim and no icon when passed both, and does not apply no-header class', () => { + const { getByTestId } = render( + + {testChildren} + + ) + 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' + ) + }) +}) diff --git a/src/components/SiteAlert/SiteAlert.tsx b/src/components/SiteAlert/SiteAlert.tsx new file mode 100644 index 0000000000..67639a75b6 --- /dev/null +++ b/src/components/SiteAlert/SiteAlert.tsx @@ -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 ( +
+
+
+ {heading &&

{heading}

} + {children} +
+
+
+ ) +} + +export default SiteAlert diff --git a/src/index.ts b/src/index.ts index 15899dc348..81f5072761 100644 --- a/src/index.ts +++ b/src/index.ts @@ -90,6 +90,8 @@ export { StepIndicatorStep } from './components/stepindicator/StepIndicatorStep/ export { Search } from './components/Search/Search' +export { SiteAlert } from './components/SiteAlert/SiteAlert' + /** Truss-designed components */ export { Modal,