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

MCR-4322 Fix mailto link to email help desk #2694

Merged
merged 11 commits into from
Aug 27, 2024
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useEffect } from 'react'
import { Alert } from '@trussworks/react-uswds'
import { useStringConstants } from '../../../hooks/useStringConstants'

import classnames from 'classnames'
import { useTealium } from '../../../hooks'
import { ContactSupportLink } from '../../ErrorAlert/ContactSupportLink'

export const DocumentWarningBanner = ({
className,
}: React.HTMLAttributes<HTMLDivElement>): React.ReactElement => {
const stringConstants = useStringConstants()
const { logAlertImpressionEvent } = useTealium()

useEffect(() => {
Expand All @@ -20,7 +20,6 @@ export const DocumentWarningBanner = ({
})
}, [logAlertImpressionEvent])

const MAIL_TO_SUPPORT = stringConstants.MAIL_TO_SUPPORT
return (
<Alert
role="alert"
Expand All @@ -34,14 +33,7 @@ export const DocumentWarningBanner = ({
Some documents aren’t available right now. Refresh the page to
try again. If you still see this message,&nbsp;
</span>
<a
href={`mailto: ${MAIL_TO_SUPPORT}, mc-review-team@truss.works`}
className="usa-link"
target="_blank"
rel="noreferrer"
>
email the help desk.
</a>
<ContactSupportLink />
</Alert>
)
}
Original file line number Diff line number Diff line change
@@ -1,70 +1,22 @@
import React, { useEffect } from 'react'
import styles from '../Banner.module.scss'
import { Alert } from '@trussworks/react-uswds'
import { ERROR_MESSAGES } from '../../../constants/errors'
import { useStringConstants } from '../../../hooks/useStringConstants'
import { LinkWithLogging } from '../../TealiumLogging/Link'
import { useTealium } from '../../../hooks'
import React from 'react'
import { ErrorAlertFailedRequest } from '../../ErrorAlert'
import { ErrorAlertValidationError } from '../../ErrorAlert/ErrorAlertValidationError'

export type GenericApiErrorProps = {
heading?: string
message?: string
suggestion?: string
validationFail?: boolean
}

export const GenericApiErrorBanner = ({
heading,
message,
suggestion,
validationFail = false,
}: GenericApiErrorProps): React.ReactElement => {
const stringConstants = useStringConstants()
const { logAlertImpressionEvent } = useTealium()
const MAIL_TO_SUPPORT = stringConstants.MAIL_TO_SUPPORT

useEffect(() => {
logAlertImpressionEvent({
error_type: 'system',
error_message:
'Please refresh your browser and if you continue to experience an error let us know.',
type: 'error',
extension: 'react-uswds',
})
}, [logAlertImpressionEvent])

if (validationFail){
return <ErrorAlertValidationError heading={heading} message={message} />
}
return (
<Alert
role="alert"
type="error"
heading={heading || 'System error'}
headingLevel="h4"
validation
data-testid="error-alert"
>
<div className={styles.bannerBodyText}>
<p className="usa-alert__text">
<b>{message || ERROR_MESSAGES.generic_error}</b>
</p>
<p className="usa-alert__text">
{suggestion ? (
<span>{suggestion} </span>
) : (
<>
<span>
Please refresh your browser and if you continue
to experience an error,&nbsp;
</span>
<LinkWithLogging
href={`mailto: ${MAIL_TO_SUPPORT}, mc-review-team@truss.works`}
variant="unstyled"
target="_blank"
rel="noreferrer"
>
let us know.
</LinkWithLogging>
</>
)}
</p>
</div>
</Alert>
<ErrorAlertFailedRequest heading={heading} message={message}/>
)
}
27 changes: 27 additions & 0 deletions services/app-web/src/components/ErrorAlert/ContactSupportLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'
import { useStringConstants } from '../../hooks/useStringConstants'
import { LinkWithLogging } from '../TealiumLogging/Link'

type ContactSupportLinkProps = {
alternateText?: string
className?: string
}

export const ContactSupportLink = ({
alternateText,
className
}: ContactSupportLinkProps): React.ReactElement => {
const stringConstants = useStringConstants()
const displayText = alternateText?? 'email the help desk'
return (
<LinkWithLogging
className={className}
variant="unstyled"
href={stringConstants.MAIL_TO_SUPPORT_HREF}
target="_blank"
rel="noreferrer"
>
{displayText}
</LinkWithLogging>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ErrorAlertFailedRequest } from './ErrorAlertFailedRequest'
import { ErrorAlertSessionExpired } from './ErrorAlertSessionExpired'
import { ErrorAlertSignIn } from './ErrorAlertSignIn'
import { ErrorAlertSiteUnavailable } from './ErrorAlertSiteUnavailable'
import { ErrorAlertScheduledMaintenance } from './ErrorAlertScheduledMaintenance'
import { ErrorAlertValidationError } from './ErrorAlertValidationError'

export default {
title: 'Components/ErrorAlert',
Expand All @@ -14,19 +16,21 @@ const Template: StoryFn<ErrorAlertProps> = (args) => <ErrorAlert {...args} />

export const Default = Template.bind({})

export const CustomStylesWithLetUsKnowLink = Template.bind({})
CustomStylesWithLetUsKnowLink.args = {
export const CustomStylesWithContactSupportLink = Template.bind({})
CustomStylesWithContactSupportLink.args = {
message:
'Here is an error alert with extra padding and a light gray background. If you see anything odd,',
'Here is an error alert with extra padding and a light gray background.',
style: { backgroundColor: '#F0F0F0', padding: '2em' },
appendLetUsKnow: true,
remediation: 'DEFAULT'
}

// List of application context-specific error alert components for quick reference.
export const ListOfApplicationErrorAlerts = (): React.ReactElement => (
<div className="sb-padded">
<ErrorAlertFailedRequest />
<ErrorAlertValidationError />
<ErrorAlertSiteUnavailable />
<ErrorAlertScheduledMaintenance/>
<ErrorAlertSignIn />
<ErrorAlertSessionExpired />
</div>
Expand Down
27 changes: 27 additions & 0 deletions services/app-web/src/components/ErrorAlert/ErrorAlert.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { screen } from '@testing-library/react'
import { ErrorAlert } from './ErrorAlert'
import { renderWithProviders } from '../../testHelpers'
import { useStringConstants } from '../../hooks/useStringConstants'

test('renders default content when no props present', () => {
renderWithProviders(<ErrorAlert />)
Expand Down Expand Up @@ -31,6 +32,9 @@ test('renders custom message', async () => {
expect(
screen.queryByText("We're having trouble loading this page.")
).toBeNull()
expect(
screen.getByText(/Something else went wrong/)
).not.toHaveStyle('font-weight: bold')
})

test('renders with custom styles', async () => {
Expand All @@ -41,3 +45,26 @@ test('renders with custom styles', async () => {
await expect(screen.getByText(testText)).toBeInTheDocument()
expect(screen.queryByTestId('error-alert')).toHaveClass('test-class')
})
test('displays email support link mailto link with default remediation', () =>{
const stringConstants = useStringConstants()
renderWithProviders(
<ErrorAlert remediation='DEFAULT' />)
const feedbackLink = screen.getByRole('link', {
name: `email the help desk`,
})
expect(feedbackLink).toHaveAttribute(
'href',
stringConstants.MAIL_TO_SUPPORT_HREF
)
})

test('displays message with bold text when withEmphasis is used', () =>{
renderWithProviders(
<ErrorAlert remediation='DEFAULT' withEmphasis />)
expect(
screen.queryByText("We're having trouble loading this page.")
).toHaveStyle('font-weight: bold')
expect(
screen.queryByText("email the help desk")
).not.toHaveStyle('font-weight: bold')
})
75 changes: 42 additions & 33 deletions services/app-web/src/components/ErrorAlert/ErrorAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,49 @@ import classnames from 'classnames'
import React, { useEffect } from 'react'
import styles from './ErrorAlert.module.scss'
import { Alert } from '@trussworks/react-uswds'
import { useStringConstants } from '../../hooks/useStringConstants'
import { LinkWithLogging } from '../TealiumLogging/Link'
import { useTealium } from '../../hooks'
import { extractText } from '../TealiumLogging/tealiamLoggingHelpers'
import { ErrorRemediation, RemediationType } from './ErrorRemediations'

export type ErrorAlertProps = {
message?: React.ReactNode
heading?: string
calltoAction?: React.ReactNode
appendLetUsKnow?: boolean
heading?: string // Has a default for generic error
message?: React.ReactNode // Has a default for generic error.
withEmphasis?: boolean
remediation?: RemediationType
} & React.JSX.IntrinsicElements['div']

/*
Error alert is main error display in the application.
It is used for variety of purposes including API error handling and displaying full page messages during maintainece.

A heading and a message is always displayed with fallbacks to a generic system error.

An optional remediation message may displayed in a second paragraph. Remediation includes specific instructions about what the user can do to
fix the errors, such as refreshing page or contacting support. A mailto link to contact the help desk is often included in the remediation message as well.

If the alert is displayed withEmphasis, the message is defined into two paragraphs, the first paragraph is displayed bold.
*/

export const ErrorAlert = ({
message,
heading,
appendLetUsKnow = false,
heading = 'System error',
message = "We're having trouble loading this page.",
remediation,
withEmphasis,
className,
...divProps
}: ErrorAlertProps): React.ReactElement => {
const stringConstants = useStringConstants()
const { logAlertImpressionEvent } = useTealium()
const MAIL_TO_SUPPORT = stringConstants.MAIL_TO_SUPPORT
const classes = classnames(styles.messageBodyText, className)
const showLink = appendLetUsKnow || !message // our default message includes the link
const defaultMessage =
"We're having trouble loading this page. Please refresh your browser and if you continue to experience an error,"
const logErrorMessage = `${message ? extractText(message) : defaultMessage} email ${MAIL_TO_SUPPORT}`
const loggingErrorMessage = extractText(message)

useEffect(() => {
logAlertImpressionEvent({
error_type: 'system',
error_message: logErrorMessage,
error_message: loggingErrorMessage,
type: 'error',
extension: 'react-uswds',
})
}, [logAlertImpressionEvent,logErrorMessage])
}, [loggingErrorMessage, logAlertImpressionEvent])

return (
<Alert
Expand All @@ -46,25 +53,27 @@ export const ErrorAlert = ({
heading={heading || 'System error'}
headingLevel="h4"
data-testid="error-alert"
validation
className={classes}
{...divProps}
>
<span>{message || defaultMessage}</span>

{showLink && (
<span>
&nbsp;email{' '}
<LinkWithLogging
className={styles.nowrap}
href={MAIL_TO_SUPPORT}
variant="unstyled"
target="_blank"
rel="noreferrer"
>
{MAIL_TO_SUPPORT}
</LinkWithLogging>
</span>
)}
<div className={styles.messageBodyText}>
{withEmphasis ? (
<>
<p className="usa-alert__text">
<b>{message}</b>
</p>
<p className="usa-alert__text">
<ErrorRemediation type={remediation} />
</p>
</>
) : (
<p className="usa-alert__text">
{message}&nbsp;
<ErrorRemediation type={remediation} />
</p>
)}
</div>
</Alert>
)
}
Loading
Loading