-
Notifications
You must be signed in to change notification settings - Fork 125
/
AlertBackgroundBox.jsx
171 lines (153 loc) · 5.45 KB
/
AlertBackgroundBox.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/** TODO
* This component requires a further decision on how to handle the alertList.
* Currently, we are displaying the most recent alert marked as active.
* We may want to display all active alerts, or only the most recent alert.
* Multiple alerts are not currently supported.
* They can be displayed as a list in one alert box, or as multiple alert boxes.
*/
/**
* Added accessibility fix to ensure that the alert content and the current location are
* announced to the user in a way that's accessible to screen readers.
* This component uses @prop lastPathName to check if url location is on
* the secure messages landing page so that if there's a service outage, a unique server
* error message from api response content will be displayed only for that page.
* Additionally, A11Y reccommends that the 503 error alert content should use an h1 tag
* since in this case there are no other content on screen.
*/
import React, { useEffect, useState, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import PropTypes from 'prop-types';
import { VaAlert } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import { focusElement } from 'platform/utilities/ui';
import useInterval from '../../hooks/use-interval';
import { Alerts, Errors } from '../../util/constants';
import { closeAlert, focusOutAlert } from '../../actions/alerts';
import { retrieveFolder } from '../../actions/folders';
import { formatPathName } from '../../util/helpers';
const AlertBackgroundBox = props => {
const dispatch = useDispatch();
const alertList = useSelector(state => state.sm.alerts?.alertList);
const folder = useSelector(state => state.sm.folders?.folder);
const [activeAlert, setActiveAlert] = useState(null);
const [alertContent, setAlertContent] = useState('');
const alertRef = useRef();
const {
Message: { SERVER_ERROR_503 },
} = Alerts;
const {
Code: { SERVICE_OUTAGE },
} = Errors;
const location = useLocation();
const SrOnlyTag = alertContent === SERVER_ERROR_503 ? 'h1' : 'span';
useEffect(
() => {
if (alertList?.length) {
const filteredSortedAlerts = alertList
.filter(alert => alert?.isActive)
.sort((a, b) => {
// Sort chronologically descending.
return b.datestamp - a.datestamp;
});
// The activeAlert is the most recent alert marked as active.
setActiveAlert(filteredSortedAlerts[0] || null);
}
},
[alertList],
);
const handleShowIcon = () => {
if (props.noIcon) {
return 'false';
}
return 'true';
};
const closeAlertBox = () => {
dispatch(closeAlert());
dispatch(focusOutAlert());
};
const lastPathName = formatPathName(location.pathname, 'Messages');
// these props check if the current page is the folder view page or thread view page
const foldersViewPage = /folders\/\d+/.test(location.pathname);
const threadViewPage = /thread\/\d+/.test(location.pathname);
// sets custom server error messages for the landing page and folder view pages
useEffect(
() => {
const isServiceOutage = activeAlert?.response?.code === SERVICE_OUTAGE;
const isErrorAlert = activeAlert?.alertType === 'error';
let content = activeAlert?.content;
if (
lastPathName !== 'Messages' &&
!foldersViewPage &&
!threadViewPage &&
(isServiceOutage || isErrorAlert)
) {
content = SERVER_ERROR_503;
}
setAlertContent(content);
},
[
SERVER_ERROR_503,
SERVICE_OUTAGE,
activeAlert,
foldersViewPage,
lastPathName,
location.pathname,
threadViewPage,
],
);
useInterval(() => {
const shouldRetrieveFolders =
activeAlert?.response?.code === SERVICE_OUTAGE ||
folder?.folderId === undefined;
if (shouldRetrieveFolders) {
dispatch(retrieveFolder(folder?.folderId));
dispatch(closeAlert());
}
}, 60000); // 1 minute
const alertAriaLabel = `${alertContent}. You are in ${(lastPathName ===
'Folders' &&
'My Folders') ||
(foldersViewPage && 'a custom folder view page') ||
lastPathName}.`;
return (
<>
{activeAlert && (
<VaAlert
ref={alertRef}
background-only
closeable={props.closeable}
className="vads-u-margin-bottom--1 va-alert"
close-btn-aria-label="Close notification"
disable-analytics="false"
full-width="false"
show-icon={handleShowIcon()}
status={activeAlert.alertType}
onCloseEvent={
closeAlertBox // success, error, warning, info, continue
}
onVa-component-did-load={() => {
focusElement(alertRef.current);
}}
>
<div>
<p className="vads-u-margin-y--0" data-testid="alert-text">
{alertContent}
<SrOnlyTag
className="sr-only"
aria-live="polite"
aria-atomic="true"
>
{alertAriaLabel}
</SrOnlyTag>
</p>
</div>
</VaAlert>
)}
</>
);
};
AlertBackgroundBox.propTypes = {
closeable: PropTypes.bool,
noIcon: PropTypes.bool,
};
export default AlertBackgroundBox;