From 3c5990024df6e32bc76d9f2db8a6ca6cb966f557 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 9 Nov 2021 11:18:00 -0500 Subject: [PATCH 01/24] update trusworks with modal changes --- frontend/package.json | 2 +- frontend/src/components/DeleteReportModal.css | 26 ----- frontend/src/components/DeleteReportModal.js | 67 ----------- frontend/src/components/FileUploader.js | 32 +++--- frontend/src/components/IdleModal.js | 54 ++++----- frontend/src/components/Modal.css | 21 ++-- frontend/src/components/Modal.js | 105 +++++++++--------- .../components/__tests__/DeleteReportModal.js | 75 ------------- .../src/pages/ApprovedActivityReport/index.js | 26 ++--- frontend/src/pages/Landing/MyAlerts.js | 45 ++++---- frontend/yarn.lock | 8 +- 11 files changed, 140 insertions(+), 321 deletions(-) delete mode 100644 frontend/src/components/DeleteReportModal.css delete mode 100644 frontend/src/components/DeleteReportModal.js delete mode 100644 frontend/src/components/__tests__/DeleteReportModal.js diff --git a/frontend/package.json b/frontend/package.json index ddf0606bb2..21aa79ac53 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/react-fontawesome": "^0.1.11", "@hookform/error-message": "^0.0.5", - "@trussworks/react-uswds": "1.11.0", + "@trussworks/react-uswds": "2.4.1", "@use-it/interval": "^1.0.0", "draft-js": "^0.11.7", "draftjs-to-html": "^0.9.1", diff --git a/frontend/src/components/DeleteReportModal.css b/frontend/src/components/DeleteReportModal.css deleted file mode 100644 index 75e4094818..0000000000 --- a/frontend/src/components/DeleteReportModal.css +++ /dev/null @@ -1,26 +0,0 @@ -#deleteDialog div { - border-style: none; - padding: 3px 0px 17px 15px; - margin: 7px 0px 0px 3px; - text-align: left; - line-height: 20px; - font-size: 14px; -} - -#deleteDialog h2 { - font-size: 22px; - margin-top: 20px; - margin-bottom: -10px; -} - -#deleteDialog button { - margin-left: 0px; - margin-right: 14px; - padding: 9px 38px 9px 38px; - font-size: medium; - font-weight: 600; -} - -#deleteDialog .usa-button--secondary { - background-color: #D42240; -} diff --git a/frontend/src/components/DeleteReportModal.js b/frontend/src/components/DeleteReportModal.js deleted file mode 100644 index 25682a3851..0000000000 --- a/frontend/src/components/DeleteReportModal.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useCallback, useEffect, useRef } from 'react'; -import PropTypes from 'prop-types'; -import { Button, Modal } from '@trussworks/react-uswds'; - -import { ESCAPE_KEY_CODES } from '../Constants'; -import './DeleteReportModal.css'; - -const DeleteModal = ({ - onDelete, onClose, closeModal, -}) => { - const modalRef = useRef(null); - - const onEscape = useCallback((event) => { - if (ESCAPE_KEY_CODES.includes(event.key)) { - closeModal(); - } - }, [closeModal]); - - useEffect(() => { - document.addEventListener('keydown', onEscape, false); - return () => { - document.removeEventListener('keydown', onEscape, false); - }; - }, [onEscape]); - - useEffect(() => { - const button = modalRef.current.querySelector('button'); - if (button) { - button.focus(); - } - }); - - return ( - - ); -}; - -DeleteModal.propTypes = { - onDelete: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - closeModal: PropTypes.func.isRequired, -}; - -export default DeleteModal; diff --git a/frontend/src/components/FileUploader.js b/frontend/src/components/FileUploader.js index 871e7edf16..1157e3c68e 100644 --- a/frontend/src/components/FileUploader.js +++ b/frontend/src/components/FileUploader.js @@ -11,7 +11,7 @@ import { useDropzone } from 'react-dropzone'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { - Button, Alert, Modal, connectModal, + Button, Alert, Modal, ModalToggleButton, } from '@trussworks/react-uswds'; import { uploadFile, deleteFile } from '../fetchers/File'; @@ -157,12 +157,12 @@ export const getStatus = (status) => { }; const DeleteFileModal = ({ - onFileRemoved, files, index, closeModal, + modalRef, onFileRemoved, files, index, }) => { const deleteModal = useRef(null); - const onClose = () => { + const onDeleteFile = () => { onFileRemoved(index) - .then(closeModal()); + .then(modalRef.current.toggleModal(false)); }; useEffect(() => { deleteModal.current.querySelector('button').focus(); @@ -173,10 +173,10 @@ const DeleteFileModal = ({ title={

Delete File

} actions={( <> - - @@ -196,32 +196,30 @@ const DeleteFileModal = ({ }; DeleteFileModal.propTypes = { + modalRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]).isRequired, onFileRemoved: PropTypes.func.isRequired, - closeModal: PropTypes.func.isRequired, index: PropTypes.number.isRequired, files: PropTypes.arrayOf(PropTypes.object).isRequired, }; -const ConnectedDeleteFileModal = connectModal(DeleteFileModal); - const FileTable = ({ onFileRemoved, files }) => { const [index, setIndex] = useState(null); - const [isOpen, setIsOpen] = useState(false); - const closeModal = () => setIsOpen(false); - + const modalRef = useRef(); const handleDelete = (newIndex) => { setIndex(newIndex); - setIsOpen(true); + modalRef.current.toggleModal(true); }; return (
- diff --git a/frontend/src/components/IdleModal.js b/frontend/src/components/IdleModal.js index 5c25e0deb6..c49e12c448 100644 --- a/frontend/src/components/IdleModal.js +++ b/frontend/src/components/IdleModal.js @@ -4,12 +4,11 @@ milliseconds the logout prop is called. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { useIdleTimer } from 'react-idle-timer'; -import { - Button, Modal, connectModal, useModal, Alert, -} from '@trussworks/react-uswds'; +import { Alert } from '@trussworks/react-uswds'; +import Modal from './Modal'; // list of events to determine activity // https://github.com/SupremeTechnopriest/react-idle-timer#default-events @@ -28,7 +27,7 @@ const EVENTS = [ function IdleModal({ modalTimeout, logoutTimeout, logoutUser }) { const [inactiveTimeout, updateInactiveTimeout] = useState(); - const { isOpen, openModal, closeModal } = useModal(); + const modalRef = useRef(); const modalVisibleTime = logoutTimeout - modalTimeout; const timeoutMinutes = Math.floor(modalVisibleTime / 1000 / 60); @@ -41,28 +40,6 @@ function IdleModal({ modalTimeout, logoutTimeout, logoutUser }) { timeToLogoutMsg = `${timeoutMinutes} minutes`; } - const Connected = connectModal(() => ( - Are you still there?} - actions={( - - )} - > - - You will be automatically logged out due to inactivity in - {' '} - { timeToLogoutMsg } - {' '} - unless you become active again. - - Press any key to continue your session - - - - )); - // Make sure we clean up any timeout functions when this component // is unmounted useEffect(() => function cleanup() { @@ -73,15 +50,15 @@ function IdleModal({ modalTimeout, logoutTimeout, logoutUser }) { const onIdle = () => { const timer = setTimeout(() => { - closeModal(); + modalRef.current.toggleModal(false); logoutUser(true); }, modalVisibleTime); - openModal(); + modalRef.current.toggleModal(true); updateInactiveTimeout(timer); }; const onActive = () => { - closeModal(); + modalRef.current.toggleModal(false); clearTimeout(inactiveTimeout); }; @@ -94,7 +71,22 @@ function IdleModal({ modalTimeout, logoutTimeout, logoutUser }) { }); return ( - + {}} + modalId="IdleReportModal" + title="Are you still there?" + showOkButton={false} + cancelButtonText="Stay logged in" + > + + You will be automatically logged out due to inactivity in + {' '} + {timeToLogoutMsg} + {' '} + unless you become active again. + + ); } diff --git a/frontend/src/components/Modal.css b/frontend/src/components/Modal.css index 5d33eb0e3e..d3c4590178 100644 --- a/frontend/src/components/Modal.css +++ b/frontend/src/components/Modal.css @@ -1,16 +1,5 @@ -#popup-modal div { - border-style: none; - padding: 3px 0px 17px 15px; - margin: 7px 0px 0px 3px; - text-align: left; - line-height: 1.2; - font-size: 14px; -} - #popup-modal h2 { - font-size: 22px; - margin-top: 20px; - margin-bottom: -10px; + font-family: 'Source Sans Pro Web', 'Helvetica Neue', Helvetica, 'Roboto, Arial', sans-serif; } #popup-modal button { @@ -21,6 +10,10 @@ font-weight: 600; } -#popup-modal .usa-button--secondary { - background-color: #D42240; +.popup-modal:not(.show-close-x) .usa-modal__close { + display: none; } + +.popup-modal:not(.show-ok-button) .usa-button--secondary { + display: none; +} \ No newline at end of file diff --git a/frontend/src/components/Modal.js b/frontend/src/components/Modal.js index cf4114246f..9475b63398 100644 --- a/frontend/src/components/Modal.js +++ b/frontend/src/components/Modal.js @@ -1,68 +1,71 @@ -import React, { useCallback, useEffect, useRef } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Modal as TrussWorksModal } from '@trussworks/react-uswds'; -import { ESCAPE_KEY_CODES } from '../Constants'; +import { + Button, Modal as TrussWorksModal, ModalHeading, ModalFooter, ButtonGroup, ModalToggleButton, +} from '@trussworks/react-uswds'; import './Modal.css'; const Modal = ({ - onOk, onClose, closeModal, title, okButtonText, okButtonAriaLabel, children, -}) => { - const modalRef = useRef(null); - - const onEscape = useCallback((event) => { - if (ESCAPE_KEY_CODES.includes(event.key)) { - closeModal(); - } - }, [closeModal]); - - useEffect(() => { - document.addEventListener('keydown', onEscape, false); - return () => { - document.removeEventListener('keydown', onEscape, false); - }; - }, [onEscape]); - - useEffect(() => { - const button = modalRef.current.querySelector('button'); - if (button) { - button.focus(); - } - }); - - return ( - +); Modal.propTypes = { - onOk: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - closeModal: PropTypes.func.isRequired, + modalRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.elementType }), + ]).isRequired, + modalId: PropTypes.number.isRequired, + onOk: PropTypes.func, title: PropTypes.string.isRequired, - okButtonText: PropTypes.string.isRequired, + okButtonText: PropTypes.string, okButtonAriaLabel: PropTypes.string, + showOkButton: PropTypes.bool, + cancelButtonText: PropTypes.string, + showCloseX: PropTypes.bool, children: PropTypes.node.isRequired, }; Modal.defaultProps = { + onOk: () => {}, okButtonAriaLabel: null, + okButtonText: '', + showCloseX: false, + showOkButton: true, + cancelButtonText: 'Cancel', + }; export default Modal; diff --git a/frontend/src/components/__tests__/DeleteReportModal.js b/frontend/src/components/__tests__/DeleteReportModal.js deleted file mode 100644 index bfedf09546..0000000000 --- a/frontend/src/components/__tests__/DeleteReportModal.js +++ /dev/null @@ -1,75 +0,0 @@ -import '@testing-library/jest-dom'; -import React from 'react'; -import { - render, screen, -} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { useModal, connectModal, Button } from '@trussworks/react-uswds'; - -import DeleteReportModal from '../DeleteReportModal'; - -const SomeComponent = () => { - const { isOpen, openModal, closeModal } = useModal(); - const ConnectModal = connectModal(DeleteReportModal); - - return ( -
- {}} - onClose={() => {}} - closeModal={closeModal} - isOpen={isOpen} - /> - -
- ); -}; - -describe('DeleteReportModal', () => { - it('shows two buttons', async () => { - // Given a page with a modal - render( {}} - onClose={() => {}} - closeModal={() => {}} - isOpen - />); - // When the modal is triggered - const buttons = await screen.findAllByRole('button'); - - // Then we see our options - expect(buttons.length).toBe(2); - }); - - it('exits when escape key is pressed', async () => { - // Given a page with a modal - render(); - - // When the modal is triggered - const button = await screen.findByText('Open'); - userEvent.click(button); - - const modal = await screen.findByTestId('modal'); - expect(modal).toBeVisible(); - - // And the modal can closeclose the modal via the escape key - userEvent.type(modal, '{esc}', { skipClick: true }); - expect(screen.queryByTestId('modal')).not.toBeTruthy(); - }); - - it('does not escape when any other key is pressed', async () => { - // Given a page with a modal - render(); - - // When the modal is triggered - const button = await screen.findByText('Open'); - userEvent.click(button); - - const modal = await screen.findByTestId('modal'); - expect(modal).toBeVisible(); - - // And the modal can closeclose the modal via the escape key - userEvent.type(modal, '{enter}', { skipClick: true }); - expect(screen.queryByTestId('modal')).toBeTruthy(); - }); -}); diff --git a/frontend/src/pages/ApprovedActivityReport/index.js b/frontend/src/pages/ApprovedActivityReport/index.js index b47b92fbb0..1f108dd26d 100644 --- a/frontend/src/pages/ApprovedActivityReport/index.js +++ b/frontend/src/pages/ApprovedActivityReport/index.js @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import PropTypes from 'prop-types'; import ReactRouterPropTypes from 'react-router-prop-types'; -import { Grid, useModal, connectModal } from '@trussworks/react-uswds'; +import { Grid, ModalToggleButton } from '@trussworks/react-uswds'; import { Redirect } from 'react-router-dom'; import moment from 'moment-timezone'; import { Helmet } from 'react-helmet'; @@ -145,11 +145,10 @@ export default function ApprovedActivityReport({ match, user }) { const [granteeNextSteps, setGranteeNextSteps] = useState([]); const [specialistNextSteps, setSpecialistNextSteps] = useState([]); - const { isOpen, openModal, closeModal } = useModal(); - const ConnectModal = connectModal(Modal); - const [justUnlocked, updatedJustUnlocked] = useState(false); + const modalRef = useRef(); + useEffect(() => { const allowedRegions = allRegionsUserHasPermissionTo(user); @@ -271,7 +270,7 @@ export default function ApprovedActivityReport({ match, user }) { const onUnlock = async () => { await unlockReport(reportId); - closeModal(); + modalRef.current.toggleModal(false); updatedJustUnlocked(true); }; @@ -323,19 +322,16 @@ export default function ApprovedActivityReport({ match, user }) { : null} {navigator && navigator.clipboard - ? + ? : null} - + {user && user.permissions && canUnlockReports(user) - ? + ? Unlock Report : null} - onUnlock()} - onClose={closeModal} - isOpen={isOpen} - openModal={openModal} - closeModal={closeModal} modalId="UnlockReportModal" title="Unlock Activity Report" okButtonText="Unlock" @@ -354,7 +350,7 @@ export default function ApprovedActivityReport({ match, user }) {
must be re-submitted for approval. -
+

TTA Activity report diff --git a/frontend/src/pages/Landing/MyAlerts.js b/frontend/src/pages/Landing/MyAlerts.js index 77b858de56..545402119f 100644 --- a/frontend/src/pages/Landing/MyAlerts.js +++ b/frontend/src/pages/Landing/MyAlerts.js @@ -1,12 +1,9 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; -import { - Tag, Table, useModal, connectModal, -} from '@trussworks/react-uswds'; +import { Tag, Table } from '@trussworks/react-uswds'; import { Link, useHistory } from 'react-router-dom'; - -import DeleteReportModal from '../../components/DeleteReportModal'; +import Modal from '../../components/Modal'; import Container from '../../components/Container'; import ContextMenu from '../../components/ContextMenu'; import NewReport from './NewReport'; @@ -22,15 +19,12 @@ import Tooltip from '../../components/Tooltip'; function ReportsRow({ reports, removeAlert, message }) { const history = useHistory(); - const { isOpen, openModal, closeModal } = useModal(); - const ConnectModal = connectModal(DeleteReportModal); - const [idToDelete, updateIdToDelete] = useState(0); + const modalRef = useRef(); const onDelete = async (reportId) => { await deleteReport(reportId); removeAlert(reportId); - closeModal(); }; const tableRows = reports.map((report, index, { length }) => { @@ -74,7 +68,7 @@ function ReportsRow({ reports, removeAlert, message }) { }, { label: 'Delete', - onClick: () => { updateIdToDelete(id); openModal(); }, + onClick: () => { updateIdToDelete(id); modalRef.current.toggleModal(true); }, }, ]; @@ -108,7 +102,7 @@ function ReportsRow({ reports, removeAlert, message }) { buttonLabel={`pending approvals: ${approversToolTipText}. Click button to visually reveal this information.`} /> ) - : '' } + : ''}

- onDelete(idToDelete)} - onClose={closeModal} - isOpen={isOpen} - openModal={openModal} - closeModal={closeModal} - /> + onDelete(idToDelete)} + modalId="DeleteReportModal" + title="Delete Activity Report" + okButtonText="Delete" + okButtonAriaLabel="This button will permanently delete the report." + > +
+ Are you sure you want to delete this activity report? +
+ This action + {' '} + cannot + {' '} + be undone. +
+
{tableRows} ); @@ -219,7 +224,7 @@ function MyAlerts(props) { onKeyPress={() => sortHandler(name)} className={`sortable ${sortClassName}`} aria-label={`${displayName}. Activate to sort ${sortClassName === 'asc' ? 'descending' : 'ascending' - }`} + }`} > {displayName} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d89cee3044..f960629938 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1976,10 +1976,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@trussworks/react-uswds@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@trussworks/react-uswds/-/react-uswds-1.11.0.tgz#f5f01247038792e8fec40c828e32bc78bcc04c95" - integrity sha512-wIFzLM/1aJ0enyt42YFcJRSlEALfRU2qPaVK5955mHmP083l9o2CrnmJDK7La8ebrNxtyhhgrx76K3PwFgRCjQ== +"@trussworks/react-uswds@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@trussworks/react-uswds/-/react-uswds-2.4.1.tgz#62604c86940492564465286c8214ae22a4bb5e2b" + integrity sha512-jjzBV3nVH3ee0UhWJmQr8+5k/bOTQREUWMi0U75dhF6z+kalw20c9yWBA4DlsE6ePu5d55y3ye2Rz7F+4dO+dA== "@types/aria-query@^4.2.0": version "4.2.2" From 6f59aa46d038c545a7c6dc468ce7ade4a9fe3130 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 9 Nov 2021 16:03:07 -0500 Subject: [PATCH 02/24] updated file uploader modal --- frontend/src/components/FileUploader.js | 65 ++++++++++++------------- frontend/src/components/Modal.js | 4 +- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/frontend/src/components/FileUploader.js b/frontend/src/components/FileUploader.js index 1157e3c68e..be4bc32c4c 100644 --- a/frontend/src/components/FileUploader.js +++ b/frontend/src/components/FileUploader.js @@ -5,15 +5,14 @@ // react-dropzone examples all use prop spreading. Disabling the eslint no prop spreading // rules https://github.com/react-dropzone/react-dropzone /* eslint-disable react/jsx-props-no-spreading */ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import { useDropzone } from 'react-dropzone'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; -import { - Button, Alert, Modal, ModalToggleButton, -} from '@trussworks/react-uswds'; +import { Button, Alert } from '@trussworks/react-uswds'; import { uploadFile, deleteFile } from '../fetchers/File'; +import Modal from './Modal'; import './FileUploader.css'; @@ -111,17 +110,17 @@ function Dropzone(props) { Upload Resources {errorMessage - && ( - - This is an error - - )} + && ( + + This is an error + + )} {fileRejections.length > 0 - && ( - - - - )} + && ( + + + + )} ); } @@ -159,52 +158,48 @@ export const getStatus = (status) => { const DeleteFileModal = ({ modalRef, onFileRemoved, files, index, }) => { - const deleteModal = useRef(null); const onDeleteFile = () => { onFileRemoved(index) .then(modalRef.current.toggleModal(false)); }; - useEffect(() => { - deleteModal.current.querySelector('button').focus(); - }); + return ( -
+ <> Delete File} - actions={( - <> - - Cancel - - - - )} + modalRef={modalRef} + onOk={onDeleteFile} + modalId="DeleteFileModal" + title="Delete File" + okButtonText="Delete" + okButtonAriaLabel="This button will permanently delete the file." >

Are you sure you want to delete {' '} - {files[index].originalFileName} + { files[index] ? files[index].originalFileName : null } {' '} ?

This action cannot be undone.

-
+ ); }; DeleteFileModal.propTypes = { modalRef: PropTypes.oneOfType([ PropTypes.func, - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + PropTypes.shape(), ]).isRequired, onFileRemoved: PropTypes.func.isRequired, - index: PropTypes.number.isRequired, + index: PropTypes.number, files: PropTypes.arrayOf(PropTypes.object).isRequired, }; +DeleteFileModal.defaultProps = { + index: null, +}; + const FileTable = ({ onFileRemoved, files }) => { const [index, setIndex] = useState(null); const modalRef = useRef(); @@ -271,7 +266,7 @@ const FileTable = ({ onFileRemoved, files }) => {
{files.length === 0 && ( -

No files uploaded

+

No files uploaded

)}
); diff --git a/frontend/src/components/Modal.js b/frontend/src/components/Modal.js index 9475b63398..6d5b211af2 100644 --- a/frontend/src/components/Modal.js +++ b/frontend/src/components/Modal.js @@ -45,9 +45,9 @@ const Modal = ({ Modal.propTypes = { modalRef: PropTypes.oneOfType([ PropTypes.func, - PropTypes.shape({ current: PropTypes.elementType }), + PropTypes.shape(), ]).isRequired, - modalId: PropTypes.number.isRequired, + modalId: PropTypes.string.isRequired, onOk: PropTypes.func, title: PropTypes.string.isRequired, okButtonText: PropTypes.string, From 78c046afb4b8f165ae87a4e6bbe56963f9830229 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 9 Nov 2021 18:12:58 -0500 Subject: [PATCH 03/24] updated external resource modal --- .../src/components/ExternalResourceModal.js | 99 +++++-------------- frontend/src/components/Modal.js | 4 + .../__tests__/ExternalResourceModal.js | 2 +- .../ActivityReport/Pages/Review/ReviewItem.js | 2 +- frontend/src/pages/Landing/MyAlerts.js | 2 +- 5 files changed, 31 insertions(+), 78 deletions(-) diff --git a/frontend/src/components/ExternalResourceModal.js b/frontend/src/components/ExternalResourceModal.js index 79d68f3f80..a5ce089ec7 100644 --- a/frontend/src/components/ExternalResourceModal.js +++ b/frontend/src/components/ExternalResourceModal.js @@ -1,75 +1,18 @@ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useRef } from 'react'; import PropTypes from 'prop-types'; -import { - Button, Modal, Alert, useModal, connectModal, -} from '@trussworks/react-uswds'; - +import { Alert } from '@trussworks/react-uswds'; +import Modal from './Modal'; import { isValidURL, isInternalGovernmentLink } from '../utils'; -const ESCAPE_KEY_CODE = 27; - -const ExternalResourceModal = ({ onOpen, onClose }) => ( - External Resources Disclaimer} - actions={( - <> - - - - - )} - > - - Note: - {' '} - This link is hosted outside of an OHS-led system. - OHS does not have responsibility for external content or - the privacy policies of non-government websites. - - -); - -ExternalResourceModal.propTypes = { - onOpen: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, -}; - const ExternalLink = ({ to, children }) => { const modalRef = useRef(null); - const { isOpen, openModal, closeModal } = useModal(); - - const onEscape = useCallback((event) => { - if (event.keyCode === ESCAPE_KEY_CODE) { - closeModal(); - } - }, [closeModal]); - - useEffect(() => { - document.addEventListener('keydown', onEscape, false); - return () => { - document.removeEventListener('keydown', onEscape, false); - }; - }, [onEscape]); - - useEffect(() => { - if (!modalRef.current) return; - - const button = modalRef.current.querySelector('button'); - if (button) { - button.focus(); - } - }); if (!isValidURL(to)) { return to; } - const onClick = () => { - closeModal(); + const openResource = () => { + modalRef.current.toggleModal(false); window.open(to, '_blank'); }; @@ -78,19 +21,29 @@ const ExternalLink = ({ to, children }) => { if (isInternalGovernmentLink(to)) { window.open(to, '_blank'); } else { - openModal(); + modalRef.current.toggleModal(true); } }; - const ConnectModal = connectModal(() => ( - - )); - return ( <> -
- -
+ + + Note: + {' '} + This link is hosted outside of an OHS-led system. + OHS does not have responsibility for external content or + the privacy policies of non-government websites. + + {children} {' '} @@ -103,8 +56,4 @@ ExternalLink.propTypes = { to: PropTypes.string.isRequired, children: PropTypes.node.isRequired, }; - -export { - ExternalResourceModal, - ExternalLink, -}; +export default ExternalLink; diff --git a/frontend/src/components/Modal.js b/frontend/src/components/Modal.js index 6d5b211af2..56c80e6ba7 100644 --- a/frontend/src/components/Modal.js +++ b/frontend/src/components/Modal.js @@ -15,12 +15,14 @@ const Modal = ({ showOkButton, cancelButtonText, showCloseX, + isLarge, children, }) => (