diff --git a/frontend/cypress/docker-compose.yml b/frontend/cypress/docker-compose.yml
index 012010bb50..c5523c6a87 100644
--- a/frontend/cypress/docker-compose.yml
+++ b/frontend/cypress/docker-compose.yml
@@ -61,6 +61,7 @@ services:
REACT_APP_BACKEND_URL: http://localhost.simplereport.gov/api
REACT_APP_OKTA_ENABLED: "true"
REACT_APP_OKTA_URL: http://cypress:8088
+ REACT_APP_DISABLE_MAINTENANCE_BANNER: "true"
volumes:
- ../:/frontend/
- ../../backend/src/main/resources/graphql:/backend/src/main/resources/graphql
diff --git a/frontend/package.json b/frontend/package.json
index 6fd52a1b3b..f90d0268a0 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -20,7 +20,7 @@
"@trussworks/react-uswds": "^2.6.0",
"@types/google-libphonenumber": "^7.4.23",
"@types/history": "^4.7.10",
- "@types/react-router-dom": "^5.1.6",
+ "@types/react-router-dom": "^5.3.3",
"@types/react-transition-group": "^4.4.4",
"@types/testing-library__jest-dom": "^5.14.2",
"apollo-upload-client": "^14.1.3",
@@ -37,8 +37,7 @@
"react-i18next": "^11.8.15",
"react-modal": "^3.13.1",
"react-redux": "^7.2.6",
- "react-router": "^5.2.0",
- "react-router-dom": "^5.2.0",
+ "react-router-dom": "^6.2.1",
"react-scripts": "^5.0.0",
"react-toastify": "^6.1.0",
"react-transition-group": "^4.4.2",
diff --git a/frontend/src/app/App.test.tsx b/frontend/src/app/App.test.tsx
index 221594649f..da5d2a5493 100644
--- a/frontend/src/app/App.test.tsx
+++ b/frontend/src/app/App.test.tsx
@@ -1,4 +1,9 @@
-import { BrowserRouter as Router, MemoryRouter, Route } from "react-router-dom";
+import {
+ BrowserRouter as Router,
+ MemoryRouter,
+ Route,
+ Routes,
+} from "react-router-dom";
import { Provider } from "react-redux";
import createMockStore, { MockStoreEnhanced } from "redux-mock-store";
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
@@ -199,7 +204,9 @@ const renderApp = (
-
+
+ } />
+
diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx
index bae836fb8e..f7cf8a2d45 100644
--- a/frontend/src/app/App.tsx
+++ b/frontend/src/app/App.tsx
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { gql, useQuery } from "@apollo/client";
import { useDispatch, connect } from "react-redux";
-import { Redirect, Route, Switch } from "react-router-dom";
+import { Navigate, Route, Routes, useLocation } from "react-router-dom";
import { ApplicationInsights } from "@microsoft/applicationinsights-web";
import ProtectedRoute from "./commonComponents/ProtectedRoute";
@@ -50,13 +50,14 @@ export const WHOAMI_QUERY = gql`
const App = () => {
const appInsights = getAppInsights();
const dispatch = useDispatch();
+ const location = useLocation();
// Check if the user is logged in, if not redirect to Okta
if (process.env.REACT_APP_OKTA_ENABLED === "true") {
const accessToken = localStorage.getItem("access_token");
if (!accessToken) {
// If Okta login has been attempted and returned to SR with an error, don't redirect back to Okta
- const params = new URLSearchParams(window.location.hash.slice(1));
+ const params = new URLSearchParams(location.hash.slice(1));
if (params.get("error")) {
throw new Error(
params.get("error_description") || "Unknown Okta error"
@@ -118,100 +119,125 @@ const App = () => {
let homepagePath: string;
if (isSupportAdmin) {
- homepagePath = "/admin";
+ homepagePath = "admin";
} else if (isOrgAdmin) {
- homepagePath = "/dashboard";
+ homepagePath = "dashboard";
} else {
- homepagePath = "/queue";
+ homepagePath = "queue";
}
+ const canViewResults = appPermissions.results.canView;
+ const canViewPeople = appPermissions.people.canView;
+ const canEditPeople = appPermissions.people.canEdit;
+ const canViewSettings = appPermissions.settings.canView;
+
return (
<>
-
+ {process.env.REACT_APP_DISABLE_MAINTENANCE_BANNER === "true" ? null : (
+
+ )}
{process.env.REACT_APP_IS_TRAINING_SITE === "true" && (
)}
-
- {
- return ;
- }}
- />
+
(
-
- )}
+ }
/>
- {
- return ;
- }}
- requiredPermissions={appPermissions.results.canView}
- userPermissions={data.whoami.permissions}
+ } />
+ }
+ />
+ }
/>
- }
- requiredPermissions={appPermissions.results.canView}
- userPermissions={data.whoami.permissions}
+ }
+ />
+ }
/>
- {
- return ;
- }}
- requiredPermissions={appPermissions.people.canView}
- userPermissions={data.whoami.permissions}
+ }
+ />
+ }
/>
- (
-
- )}
- requiredPermissions={appPermissions.people.canEdit}
- userPermissions={data.whoami.permissions}
+ }
+ />
+ }
/>
- }
- requiredPermissions={appPermissions.people.canEdit}
- userPermissions={data.whoami.permissions}
+ }
+ />
+ }
/>
- }
+ />
+ }
/>
- }
+ />
+ }
/>
(
- }
/>
- )}
+ }
+ />
+ }
/>
-
+
>
diff --git a/frontend/src/app/HealthChecks.tsx b/frontend/src/app/HealthChecks.tsx
index fa3918d38a..608113e171 100644
--- a/frontend/src/app/HealthChecks.tsx
+++ b/frontend/src/app/HealthChecks.tsx
@@ -1,14 +1,13 @@
-import React from "react";
-import { Route } from "react-router-dom";
+import { Route, Routes } from "react-router-dom";
-const HealthChecks: React.FC<{}> = ({ match }: any) => (
- <>
- pong
} />
+const HealthChecks = () => (
+
+ pong} />
{process.env.REACT_APP_CURRENT_COMMIT}
}
+ path="commit"
+ element={{process.env.REACT_APP_CURRENT_COMMIT}
}
/>
- >
+
);
export default HealthChecks;
diff --git a/frontend/src/app/Settings/Facility/Components/OrderingProviderList.test.tsx b/frontend/src/app/Settings/Facility/Components/OrderingProviderList.test.tsx
index 02e771071f..39cef213c8 100644
--- a/frontend/src/app/Settings/Facility/Components/OrderingProviderList.test.tsx
+++ b/frontend/src/app/Settings/Facility/Components/OrderingProviderList.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { render, screen, within } from "@testing-library/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import OrderingProviderList from "./OrderingProviderList";
diff --git a/frontend/src/app/Settings/Facility/FacilityForm.tsx b/frontend/src/app/Settings/Facility/FacilityForm.tsx
index b8c7038d57..fd1dc0e61b 100644
--- a/frontend/src/app/Settings/Facility/FacilityForm.tsx
+++ b/frontend/src/app/Settings/Facility/FacilityForm.tsx
@@ -1,5 +1,4 @@
import React, { useCallback, useState } from "react";
-import { Prompt } from "react-router-dom";
import iconSprite from "../../../../node_modules/uswds/dist/img/sprite.svg";
import Button from "../../commonComponents/Button/Button";
@@ -19,6 +18,7 @@ import {
AddressConfirmationModal,
AddressSuggestionConfig,
} from "../../commonComponents/AddressConfirmationModal";
+import Prompt from "../../utils/Prompt";
import ManageDevices from "./Components/ManageDevices";
import OrderingProviderSettings from "./Components/OrderingProvider";
diff --git a/frontend/src/app/Settings/Facility/FacilityFormContainer.test.tsx b/frontend/src/app/Settings/Facility/FacilityFormContainer.test.tsx
index 71423e620c..7e6abca118 100644
--- a/frontend/src/app/Settings/Facility/FacilityFormContainer.test.tsx
+++ b/frontend/src/app/Settings/Facility/FacilityFormContainer.test.tsx
@@ -6,7 +6,7 @@ import {
import userEvent from "@testing-library/user-event";
import { MockedProvider } from "@apollo/client/testing";
import { Provider } from "react-redux";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import { getAppInsights } from "../../TelemetryService";
@@ -166,10 +166,13 @@ jest.mock("./FacilityForm", () => {
);
};
});
-jest.mock("react-router-dom", () => ({
- Redirect: () => Redirected
,
-}));
-
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: () => Redirected
,
+ };
+});
jest.mock("../../TelemetryService", () => ({
getAppInsights: jest.fn(),
}));
diff --git a/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx b/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx
index 324df87801..26f684c017 100644
--- a/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx
+++ b/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx
@@ -1,6 +1,6 @@
import { useState } from "react";
import { gql, useQuery, useMutation } from "@apollo/client";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { updateFacility } from "../../store";
@@ -191,7 +191,7 @@ const FacilityFormContainer: any = (props: Props) => {
if (props.newOrg) {
window.location.pathname = process.env.PUBLIC_URL || "";
}
- return ;
+ return ;
}
const saveFacility = async (facility: Facility) => {
diff --git a/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx b/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx
index c434504c37..db787a3390 100644
--- a/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx
+++ b/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx
@@ -1,7 +1,7 @@
import { render, screen, act } from "@testing-library/react";
import { MockedProvider } from "@apollo/client/testing";
import { Provider } from "react-redux";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import { GetManagedFacilitiesDocument } from "../../../generated/graphql";
diff --git a/frontend/src/app/Settings/FacilityForm.test.tsx b/frontend/src/app/Settings/FacilityForm.test.tsx
index 624ed120b9..8189eb6816 100644
--- a/frontend/src/app/Settings/FacilityForm.test.tsx
+++ b/frontend/src/app/Settings/FacilityForm.test.tsx
@@ -1,6 +1,6 @@
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import * as clia from "../utils/clia";
diff --git a/frontend/src/app/Settings/Settings.tsx b/frontend/src/app/Settings/Settings.tsx
index 8496dd64f3..8e5f934afc 100644
--- a/frontend/src/app/Settings/Settings.tsx
+++ b/frontend/src/app/Settings/Settings.tsx
@@ -1,4 +1,4 @@
-import { Route, RouteComponentProps, Switch } from "react-router-dom";
+import { Route, Routes, useParams } from "react-router-dom";
import { useDocumentTitle } from "../utils/hooks";
@@ -9,43 +9,34 @@ import ManageUsersContainer from "./Users/ManageUsersContainer";
import SettingsNav from "./SettingsNav";
import { ManageSelfRegistrationLinksContainer } from "./ManageSelfRegistrationLinksContainer";
-interface Params {
- facilityId: string;
-}
-
-const Settings: React.FC> = ({ match }) => {
+const Settings = () => {
useDocumentTitle("Settings");
+ const { facilityId } = useParams();
+
return (
-
-
+
+ } />
) => (
-
- )}
+ path="facility/:facilityId"
+ element={}
/>
) => (
-
- )}
+ path="add-facility/"
+ element={}
/>
}
/>
}
/>
-
-
+ } />
+
);
diff --git a/frontend/src/app/Settings/SettingsNav.tsx b/frontend/src/app/Settings/SettingsNav.tsx
index 9a6ca51417..2d18a7ba8c 100644
--- a/frontend/src/app/Settings/SettingsNav.tsx
+++ b/frontend/src/app/Settings/SettingsNav.tsx
@@ -7,24 +7,33 @@ const SettingsNav = () => {
}
+ />
+
+
+
);
});
diff --git a/frontend/src/app/accountCreation/PasswordForm/PasswordForm.tsx b/frontend/src/app/accountCreation/PasswordForm/PasswordForm.tsx
index 34aad4a064..cd55679f2f 100644
--- a/frontend/src/app/accountCreation/PasswordForm/PasswordForm.tsx
+++ b/frontend/src/app/accountCreation/PasswordForm/PasswordForm.tsx
@@ -1,5 +1,5 @@
import { ChangeEvent, useCallback, useEffect, useState } from "react";
-import { Redirect } from "react-router";
+import { Navigate } from "react-router-dom";
import TextInput from "../../commonComponents/TextInput";
import Button from "../../commonComponents/Button/Button";
@@ -166,7 +166,7 @@ export const PasswordForm = () => {
}
if (submitted) {
- return ;
+ return ;
}
return (
diff --git a/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.test.tsx b/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.test.tsx
index 463bb73e3c..e674b1b061 100644
--- a/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.test.tsx
+++ b/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.test.tsx
@@ -4,7 +4,7 @@ import {
waitForElementToBeRemoved,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import { MemoryRouter, Route } from "react-router";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
import { SecurityQuestion } from "./SecurityQuestion";
import "../../../i18n";
@@ -28,10 +28,13 @@ describe("SecurityQuestion", () => {
beforeEach(() => {
render(
-
-
- Recovery question set successfully.
-
+
+ } />
+ Recovery question set successfully.}
+ />
+
);
});
diff --git a/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.tsx b/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.tsx
index 0fd673b4b7..cba1a1e900 100644
--- a/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.tsx
+++ b/frontend/src/app/accountCreation/SecurityQuestion/SecurityQuestion.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router";
+import { Navigate } from "react-router-dom";
import { Card } from "../../commonComponents/Card/Card";
import { CardBackground } from "../../commonComponents/CardBackground/CardBackground";
@@ -71,7 +71,7 @@ export const SecurityQuestion = () => {
}
if (submitted) {
- return ;
+ return ;
}
return (
diff --git a/frontend/src/app/accountCreation/UserAccountStatus.test.ts b/frontend/src/app/accountCreation/UserAccountStatus.test.ts
index 8a526156a8..4d653e3375 100644
--- a/frontend/src/app/accountCreation/UserAccountStatus.test.ts
+++ b/frontend/src/app/accountCreation/UserAccountStatus.test.ts
@@ -2,55 +2,57 @@ import { routeFromStatus, UserAccountStatus } from "./UserAccountStatus";
describe("UserAccountStatus", () => {
it("returns initial page for user pending activation", () => {
- expect(routeFromStatus(UserAccountStatus.PENDING_ACTIVATION)).toBe("/");
+ expect(routeFromStatus(UserAccountStatus.PENDING_ACTIVATION)).toBe("/uac");
});
it("returns set-password page for password reset", () => {
expect(routeFromStatus(UserAccountStatus.PASSWORD_RESET)).toBe(
- "/set-password"
+ "/uac/set-password"
);
});
it("returns set-recovery-questions page for set security questions", () => {
expect(routeFromStatus(UserAccountStatus.SET_SECURITY_QUESTIONS)).toBe(
- "/set-recovery-question"
+ "/uac/set-recovery-question"
);
});
it("returns mfa-select page for user pending mfa selection", () => {
- expect(routeFromStatus(UserAccountStatus.MFA_SELECT)).toBe("/mfa-select");
+ expect(routeFromStatus(UserAccountStatus.MFA_SELECT)).toBe(
+ "/uac/mfa-select"
+ );
});
it("returns sms-verify page for user pending sms verification", () => {
expect(routeFromStatus(UserAccountStatus.SMS_PENDING_ACTIVATION)).toBe(
- "/mfa-sms/verify"
+ "/uac/mfa-sms/verify"
);
});
it("returns phone-verify page for user pending call verification", () => {
expect(routeFromStatus(UserAccountStatus.CALL_PENDING_ACTIVATION)).toBe(
- "/mfa-phone/verify"
+ "/uac/mfa-phone/verify"
);
});
it("returns email-verify page for user pending email verification", () => {
expect(routeFromStatus(UserAccountStatus.EMAIL_PENDING_ACTIVATION)).toBe(
- "/mfa-email/verify"
+ "/uac/mfa-email/verify"
);
});
it("returns google-auth-verify page for user pending google verification", () => {
expect(routeFromStatus(UserAccountStatus.GOOGLE_PENDING_ACTIVATION)).toBe(
- "/mfa-google-auth/verify"
+ "/uac/mfa-google-auth/verify"
);
});
it("returns okta-auth page for user pending okta verification", () => {
expect(routeFromStatus(UserAccountStatus.OKTA_PENDING_ACTIVATION)).toBe(
- "/mfa-okta/verify"
+ "/uac/mfa-okta/verify"
);
});
it("returns security-key page for user pending fido verification", () => {
expect(routeFromStatus(UserAccountStatus.FIDO_PENDING_ACTIVATION)).toBe(
- "/mfa-security-key"
+ "/uac/mfa-security-key"
);
});
it("returns success page for an active user", () => {
- expect(routeFromStatus(UserAccountStatus.ACTIVE)).toBe("/success");
+ expect(routeFromStatus(UserAccountStatus.ACTIVE)).toBe("/uac/success");
});
it("returns not-found page for an unknown state", () => {
- expect(routeFromStatus(UserAccountStatus.UNKNOWN)).toBe("/not-found");
+ expect(routeFromStatus(UserAccountStatus.UNKNOWN)).toBe("/uac/not-found");
});
});
diff --git a/frontend/src/app/accountCreation/UserAccountStatus.tsx b/frontend/src/app/accountCreation/UserAccountStatus.tsx
index 2bdbdf2183..ca7e9ec601 100644
--- a/frontend/src/app/accountCreation/UserAccountStatus.tsx
+++ b/frontend/src/app/accountCreation/UserAccountStatus.tsx
@@ -20,28 +20,28 @@ export const routeFromStatus = (userAccountStatus: UserAccountStatus) => {
switch (userAccountStatus) {
case UserAccountStatus.LOADING:
case UserAccountStatus.PENDING_ACTIVATION:
- return "/";
+ return "/uac";
case UserAccountStatus.PASSWORD_RESET:
- return "/set-password";
+ return "/uac/set-password";
case UserAccountStatus.SET_SECURITY_QUESTIONS:
- return "/set-recovery-question";
+ return "/uac/set-recovery-question";
case UserAccountStatus.MFA_SELECT:
- return "/mfa-select";
+ return "/uac/mfa-select";
case UserAccountStatus.SMS_PENDING_ACTIVATION:
- return "/mfa-sms/verify";
+ return "/uac/mfa-sms/verify";
case UserAccountStatus.CALL_PENDING_ACTIVATION:
- return "/mfa-phone/verify";
+ return "/uac/mfa-phone/verify";
case UserAccountStatus.EMAIL_PENDING_ACTIVATION:
- return "/mfa-email/verify";
+ return "/uac/mfa-email/verify";
case UserAccountStatus.GOOGLE_PENDING_ACTIVATION:
- return "/mfa-google-auth/verify";
+ return "/uac/mfa-google-auth/verify";
case UserAccountStatus.OKTA_PENDING_ACTIVATION:
- return "/mfa-okta/verify";
+ return "/uac/mfa-okta/verify";
case UserAccountStatus.FIDO_PENDING_ACTIVATION:
- return "/mfa-security-key";
+ return "/uac/mfa-security-key";
case UserAccountStatus.ACTIVE:
- return "/success";
+ return "/uac/success";
default:
- return "/not-found";
+ return "/uac/not-found";
}
};
diff --git a/frontend/src/app/commonComponents/Header.tsx b/frontend/src/app/commonComponents/Header.tsx
index e1ac01312e..2ad3f92de6 100644
--- a/frontend/src/app/commonComponents/Header.tsx
+++ b/frontend/src/app/commonComponents/Header.tsx
@@ -108,6 +108,11 @@ const Header: React.FC<{}> = () => {
);
};
+ const activeNavItem = "active-nav-item prime-nav-link";
+ const inactiveNavItem = "prime-nav-link";
+ const getNavItemClassName = ({ isActive }: { isActive: boolean }) =>
+ isActive ? activeNavItem : inactiveNavItem;
+
return (
@@ -155,11 +160,7 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
- activeStyle={{
- color: "white",
- }}
+ className={getNavItemClassName}
>
Dashboard
@@ -170,11 +171,7 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
- activeStyle={{
- color: "white",
- }}
+ className={getNavItemClassName}
>
Conduct tests
@@ -185,11 +182,7 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
- activeStyle={{
- color: "white",
- }}
+ className={getNavItemClassName}
>
Results
@@ -200,11 +193,7 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
- activeStyle={{
- color: "white",
- }}
+ className={getNavItemClassName}
>
{PATIENT_TERM_PLURAL_CAP}
@@ -251,10 +240,10 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- activeStyle={{
- color: "white",
- }}
+ className={({ isActive }) =>
+ isActive ? "active-nav-item" : ""
+ }
+ style={({ isActive }) => ({ color: isActive ? "white" : "" })}
>
Settings
@@ -270,12 +259,8 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
+ className={getNavItemClassName}
id="dashboard-nav-link"
- activeStyle={{
- color: "white",
- }}
>
Dashboard
@@ -286,12 +271,8 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
+ className={getNavItemClassName}
id="conduct-test-nav-link"
- activeStyle={{
- color: "white",
- }}
>
Conduct tests
@@ -302,12 +283,8 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
+ className={getNavItemClassName}
id="results-nav-link"
- activeStyle={{
- color: "white",
- }}
>
Results
@@ -318,12 +295,8 @@ const Header: React.FC<{}> = () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- className="prime-nav-link"
+ className={getNavItemClassName}
id="patient-nav-link"
- activeStyle={{
- color: "white",
- }}
>
{PATIENT_TERM_PLURAL_CAP}
@@ -346,12 +319,13 @@ const Header: React.FC<{}> = () => {
staffDetailsVisible}
onClick={(e) => {
e.preventDefault();
setStaffDetailsVisible(!staffDetailsVisible);
}}
- activeClassName="active-nav-item"
+ className={() =>
+ staffDetailsVisible ? activeNavItem : inactiveNavItem
+ }
data-testid="user-button"
>
= () => {
setMenuVisible(false)}
- activeClassName="active-nav-item"
- activeStyle={{
- color: "white",
- }}
+ className={({ isActive }) =>
+ isActive ? "active-nav-item" : ""
+ }
+ style={({ isActive }) => ({ color: isActive ? "white" : "" })}
>
diff --git a/frontend/src/app/commonComponents/ProtectedRoute.tsx b/frontend/src/app/commonComponents/ProtectedRoute.tsx
index fa1253cd77..cf3719d9fe 100644
--- a/frontend/src/app/commonComponents/ProtectedRoute.tsx
+++ b/frontend/src/app/commonComponents/ProtectedRoute.tsx
@@ -1,19 +1,17 @@
import React from "react";
-import { Redirect, Route } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { UserPermission } from "../../generated/graphql";
interface Props {
requiredPermissions: UserPermission[];
userPermissions: UserPermission[] | undefined;
- path: string;
- component?: React.FC;
- render?: any;
+ element: any;
}
const ProtectedRoute: React.FC = ({
requiredPermissions,
userPermissions,
- ...rest
+ element,
}) => {
let hasAccess;
if (!userPermissions) {
@@ -24,7 +22,7 @@ const ProtectedRoute: React.FC = ({
);
}
- return hasAccess ? : ;
+ return hasAccess ? element : ;
};
export default ProtectedRoute;
diff --git a/frontend/src/app/commonComponents/__test__/Header.test.tsx b/frontend/src/app/commonComponents/__test__/Header.test.tsx
index 1ef1ee7b00..d47ccb0d44 100644
--- a/frontend/src/app/commonComponents/__test__/Header.test.tsx
+++ b/frontend/src/app/commonComponents/__test__/Header.test.tsx
@@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Provider } from "react-redux";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import createMockStore from "redux-mock-store";
import Header from "../Header";
diff --git a/frontend/src/app/commonComponents/__test__/LinkWithQuery.test.tsx b/frontend/src/app/commonComponents/__test__/LinkWithQuery.test.tsx
index 469d50b638..9b85caa306 100644
--- a/frontend/src/app/commonComponents/__test__/LinkWithQuery.test.tsx
+++ b/frontend/src/app/commonComponents/__test__/LinkWithQuery.test.tsx
@@ -1,5 +1,5 @@
import { render, screen } from "@testing-library/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { LinkWithQuery } from "../LinkWithQuery";
diff --git a/frontend/src/app/facilitySelect/WithFacility.test.tsx b/frontend/src/app/facilitySelect/WithFacility.test.tsx
index 122bc8673b..2807718fb0 100644
--- a/frontend/src/app/facilitySelect/WithFacility.test.tsx
+++ b/frontend/src/app/facilitySelect/WithFacility.test.tsx
@@ -3,7 +3,7 @@ import configureStore from "redux-mock-store";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MockedProvider } from "@apollo/client/testing";
-import { MemoryRouter as Router } from "react-router";
+import { MemoryRouter as Router } from "react-router-dom";
import { I18nextProvider } from "react-i18next";
import { appPermissions } from "../permissions";
diff --git a/frontend/src/app/facilitySelect/useSelectedFacility.ts b/frontend/src/app/facilitySelect/useSelectedFacility.ts
index 2327db50d7..74568d5047 100644
--- a/frontend/src/app/facilitySelect/useSelectedFacility.ts
+++ b/frontend/src/app/facilitySelect/useSelectedFacility.ts
@@ -1,23 +1,21 @@
import { useSelector } from "react-redux";
-import { useHistory, useLocation } from "react-router";
+import { useNavigate, useLocation } from "react-router-dom";
import { RootState } from "../store";
+import { getFacilityIdFromUrl } from "../utils/url";
export function useSelectedFacility() {
- const history = useHistory();
- const facilityId = useQuery().get("facility");
+ const navigate = useNavigate();
+ const location = useLocation();
+ const facilityId = getFacilityIdFromUrl(location);
const selectedFacility = useSelector(
(state) => state.facilities.find((f) => f.id === facilityId)
);
const setSelectedFacility = (selected: Facility) => {
- history.push({ search: `?facility=${encodeURIComponent(selected.id)}` });
+ navigate({ search: `?facility=${encodeURIComponent(selected.id)}` });
};
return [selectedFacility, setSelectedFacility] as const;
}
-
-function useQuery() {
- return new URLSearchParams(useLocation().search);
-}
diff --git a/frontend/src/app/patients/AddPatient.stories.tsx b/frontend/src/app/patients/AddPatient.stories.tsx
index 73a49aca0c..e04b7887b1 100644
--- a/frontend/src/app/patients/AddPatient.stories.tsx
+++ b/frontend/src/app/patients/AddPatient.stories.tsx
@@ -1,7 +1,7 @@
import { Story, Meta } from "@storybook/react";
import { Provider } from "react-redux";
import { MockedProvider } from "@apollo/client/testing";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import AddPatient from "./AddPatient";
diff --git a/frontend/src/app/patients/AddPatient.test.tsx b/frontend/src/app/patients/AddPatient.test.tsx
index ee53ad39ae..92efd75202 100644
--- a/frontend/src/app/patients/AddPatient.test.tsx
+++ b/frontend/src/app/patients/AddPatient.test.tsx
@@ -8,7 +8,7 @@ import {
import { MockedProvider } from "@apollo/client/testing";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { MemoryRouter, Route } from "react-router";
+import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { ToastContainer } from "react-toastify";
@@ -23,7 +23,7 @@ const store = mockStore({
const RouterWithFacility: React.FC = ({ children }) => (
- {children}
+ {children}
);
@@ -76,13 +76,15 @@ describe("AddPatient", () => {
describe("No facility selected", () => {
beforeEach(() => {
render(
-
-
-
-
-
-
-
+
+
+
+
+ } />
+
+
+
+
);
});
it("does not show the form title", () => {
@@ -192,17 +194,19 @@ describe("AddPatient", () => {
},
];
+ const Queue = () => {
+ const location = useLocation();
+ return Testing Queue! {location.search}
;
+ };
+
render(
<>
-
- Patients!
} />
- Testing Queue! {p.location.search}
}
- />
+ } path={"/add-patient"} />
+ Patients!} />
+ } />
@@ -515,8 +519,8 @@ describe("AddPatient", () => {
-
- Patients!
} />
+ } path={"/add-patient/"} />
+ Patients!} />
@@ -573,8 +577,8 @@ describe("AddPatient", () => {
-
- Patients!
} />
+ } path={"/add-patient/"} />
+ Patients!} />
diff --git a/frontend/src/app/patients/AddPatient.tsx b/frontend/src/app/patients/AddPatient.tsx
index b372886193..0a0932f183 100644
--- a/frontend/src/app/patients/AddPatient.tsx
+++ b/frontend/src/app/patients/AddPatient.tsx
@@ -1,10 +1,9 @@
import React, { useEffect, useState } from "react";
import { gql, useLazyQuery, useMutation } from "@apollo/client";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import moment from "moment";
import { useSelector } from "react-redux";
-import { LocationDescriptor } from "history";
import iconSprite from "../../../node_modules/uswds/dist/img/sprite.svg";
import { PATIENT_TERM, PATIENT_TERM_CAP } from "../../config/constants";
@@ -223,11 +222,11 @@ const AddPatient = () => {
const personPath = `/patients/?facility=${activeFacilityId}`;
const [redirect, setRedirect] = useState<
- string | LocationDescriptor | undefined
+ string | { pathname: string; search: string; state?: any } | undefined
>(undefined);
if (redirect) {
- return ;
+ return ;
}
if (!activeFacilityId) {
diff --git a/frontend/src/app/patients/Components/PersonForm.tsx b/frontend/src/app/patients/Components/PersonForm.tsx
index 733cc2b301..2d5a0ab534 100644
--- a/frontend/src/app/patients/Components/PersonForm.tsx
+++ b/frontend/src/app/patients/Components/PersonForm.tsx
@@ -1,5 +1,4 @@
import React, { useCallback, useState, useEffect, useRef } from "react";
-import { Prompt } from "react-router-dom";
import { SchemaOf } from "yup";
import { useTranslation } from "react-i18next";
import { ComboBox } from "@trussworks/react-uswds";
@@ -38,6 +37,7 @@ import {
getSelectedDeliveryPreferencesEmail,
toggleDeliveryPreferenceEmail,
} from "../../utils/deliveryPreferences";
+import Prompt from "../../utils/Prompt";
import FacilitySelect from "./FacilitySelect";
import ManagePhoneNumbers from "./ManagePhoneNumbers";
diff --git a/frontend/src/app/patients/EditPatient.test.tsx b/frontend/src/app/patients/EditPatient.test.tsx
index 05fbfdd89e..042e67853f 100644
--- a/frontend/src/app/patients/EditPatient.test.tsx
+++ b/frontend/src/app/patients/EditPatient.test.tsx
@@ -9,7 +9,7 @@ import userEvent from "@testing-library/user-event";
import { MockedProvider } from "@apollo/client/testing";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import EditPatient, { GET_PATIENT } from "./EditPatient";
@@ -424,9 +424,9 @@ describe("EditPatient", () => {
describe("EditPatientContainer", () => {
it("doesn't render if no facility is provided", async () => {
render(
-
+
-
+
);
diff --git a/frontend/src/app/patients/EditPatient.tsx b/frontend/src/app/patients/EditPatient.tsx
index e26ba57593..48c0381ece 100644
--- a/frontend/src/app/patients/EditPatient.tsx
+++ b/frontend/src/app/patients/EditPatient.tsx
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import { gql, useMutation, useQuery } from "@apollo/client";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import iconSprite from "../../../node_modules/uswds/dist/img/sprite.svg";
@@ -187,7 +187,7 @@ const EditPatient = (props: Props) => {
const personPath = `/patients/?facility=${props.facilityId}`;
if (redirect) {
- return ;
+ return ;
}
if (loading) {
diff --git a/frontend/src/app/patients/EditPatientContainer.tsx b/frontend/src/app/patients/EditPatientContainer.tsx
index 6f5a8d2a8e..23aa5bf73e 100644
--- a/frontend/src/app/patients/EditPatientContainer.tsx
+++ b/frontend/src/app/patients/EditPatientContainer.tsx
@@ -1,20 +1,23 @@
-import React from "react";
+import { useParams } from "react-router-dom";
import { useSelectedFacility } from "../facilitySelect/useSelectedFacility";
import EditPatient from "./EditPatient";
-interface Props {
- patientId: string;
-}
-
-const EditPatientContainer: React.FC = ({ patientId }) => {
+const EditPatientContainer = () => {
const [facility] = useSelectedFacility();
const activeFacilityId = facility?.id;
+ const params = useParams();
+ const patientId = params.patientId || "";
if (!activeFacilityId) {
return "No facility selected"
;
}
+
+ if (!patientId) {
+ return No patient selected
;
+ }
+
return ;
};
diff --git a/frontend/src/app/patients/ManagePatients.test.tsx b/frontend/src/app/patients/ManagePatients.test.tsx
index ad62d96296..aacd03fa2c 100644
--- a/frontend/src/app/patients/ManagePatients.test.tsx
+++ b/frontend/src/app/patients/ManagePatients.test.tsx
@@ -6,7 +6,7 @@ import {
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Provider } from "react-redux";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import createMockStore from "redux-mock-store";
import ManagePatients, {
diff --git a/frontend/src/app/patients/ManagePatients.tsx b/frontend/src/app/patients/ManagePatients.tsx
index b09bab23ef..a23c6aa175 100644
--- a/frontend/src/app/patients/ManagePatients.tsx
+++ b/frontend/src/app/patients/ManagePatients.tsx
@@ -4,7 +4,7 @@ import moment from "moment";
import classnames from "classnames";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSlidersH } from "@fortawesome/free-solid-svg-icons";
-import { useHistory } from "react-router";
+import { useNavigate } from "react-router-dom";
import { displayFullName } from "../utils";
import { PATIENT_TERM, PATIENT_TERM_PLURAL_CAP } from "../../config/constants";
@@ -115,7 +115,7 @@ export const DetachedManagePatients = ({
activeFacilityId,
}: Props) => {
const [archivePerson, setArchivePerson] = useState(null);
- const history = useHistory();
+ const navigate = useNavigate();
const [queryString, debounced, setDebounced] = useDebounce(
null,
@@ -130,11 +130,11 @@ export const DetachedManagePatients = ({
} else if (!queryString) {
setNamePrefixMatch(null);
}
- history.push({
+ navigate({
pathname: `/patients/1`,
search: `?facility=${activeFacilityId}`,
});
- }, [queryString, setNamePrefixMatch, history, activeFacilityId]);
+ }, [queryString, setNamePrefixMatch, navigate, activeFacilityId]);
if (archivePerson) {
return (
diff --git a/frontend/src/app/patients/ManagePatientsContainer.tsx b/frontend/src/app/patients/ManagePatientsContainer.tsx
index ce672f1ed4..e86ebe1db0 100644
--- a/frontend/src/app/patients/ManagePatientsContainer.tsx
+++ b/frontend/src/app/patients/ManagePatientsContainer.tsx
@@ -1,4 +1,5 @@
import { useSelector } from "react-redux";
+import { useParams } from "react-router-dom";
import { useSelectedFacility } from "../facilitySelect/useSelectedFacility";
import { hasPermission, appPermissions } from "../permissions";
@@ -7,8 +8,9 @@ import { useDocumentTitle } from "../utils/hooks";
import ManagePatients from "./ManagePatients";
-const ManagePatientsContainer = (props: { page?: number }) => {
+const ManagePatientsContainer = () => {
useDocumentTitle("People");
+ const { pageNumber } = useParams();
const [facility] = useSelectedFacility();
const activeFacilityId = facility?.id || "";
const user = useSelector((state) => state.user);
@@ -32,7 +34,7 @@ const ManagePatientsContainer = (props: { page?: number }) => {
activeFacilityId={activeFacilityId}
canEditUser={canEditUser}
canDeleteUser={canDeleteUser}
- currentPage={props.page}
+ currentPage={Number(pageNumber)}
isAdmin={isAdmin}
/>
);
diff --git a/frontend/src/app/signUp/IdentityVerification/Consent.stories.tsx b/frontend/src/app/signUp/IdentityVerification/Consent.stories.tsx
index 40ab3fe7f3..dcae9a2ee7 100644
--- a/frontend/src/app/signUp/IdentityVerification/Consent.stories.tsx
+++ b/frontend/src/app/signUp/IdentityVerification/Consent.stories.tsx
@@ -1,5 +1,5 @@
import { Story, Meta } from "@storybook/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import Consent from "./Consent";
diff --git a/frontend/src/app/signUp/IdentityVerification/Consent.test.tsx b/frontend/src/app/signUp/IdentityVerification/Consent.test.tsx
index 286af71147..522c6dbd6b 100644
--- a/frontend/src/app/signUp/IdentityVerification/Consent.test.tsx
+++ b/frontend/src/app/signUp/IdentityVerification/Consent.test.tsx
@@ -1,11 +1,15 @@
import { render, screen } from "@testing-library/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import Consent from "./Consent";
-jest.mock("react-router-dom", () => ({
- Redirect: (props: any) => `Redirected to ${props.to.pathname}`,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: (props: any) => `Redirected to ${props.to}`,
+ };
+});
describe("Consent", () => {
beforeEach(() => {
diff --git a/frontend/src/app/signUp/IdentityVerification/Consent.tsx b/frontend/src/app/signUp/IdentityVerification/Consent.tsx
index 7fc9281c61..cac5c08d6a 100644
--- a/frontend/src/app/signUp/IdentityVerification/Consent.tsx
+++ b/frontend/src/app/signUp/IdentityVerification/Consent.tsx
@@ -1,6 +1,5 @@
import { useState } from "react";
-import { useLocation } from "react-router";
-import { Redirect } from "react-router-dom";
+import { useLocation, Navigate } from "react-router-dom";
import { Card } from "../../commonComponents/Card/Card";
import { CardBackground } from "../../commonComponents/CardBackground/CardBackground";
@@ -13,11 +12,11 @@ import PersonalDetailsForm, {
const Consent = () => {
// Get person name & org id from route state
const { orgExternalId, firstName, middleName, lastName } =
- useLocation().state || {};
+ (useLocation().state as PersonalDetailsFormProps) || {};
const [submitted, setSubmitted] = useState(false);
if (!orgExternalId || !firstName || !lastName) {
- return ;
+ return ;
}
if (submitted) {
diff --git a/frontend/src/app/signUp/Organization/OrganizationForm.test.tsx b/frontend/src/app/signUp/Organization/OrganizationForm.test.tsx
index 1c6330d9d5..402896ce4b 100644
--- a/frontend/src/app/signUp/Organization/OrganizationForm.test.tsx
+++ b/frontend/src/app/signUp/Organization/OrganizationForm.test.tsx
@@ -53,8 +53,8 @@ jest.mock("../SignUpApi", () => ({
},
}));
-jest.mock("react-router", () => ({
- Redirect: (props: any) => `Redirected to ${props.to.pathname}`,
+jest.mock("react-router-dom", () => ({
+ Navigate: (props: any) => `Redirected to ${props.to}`,
}));
window.scrollTo = jest.fn();
diff --git a/frontend/src/app/signUp/Organization/OrganizationForm.tsx b/frontend/src/app/signUp/Organization/OrganizationForm.tsx
index 5070a4f206..82abc691b0 100644
--- a/frontend/src/app/signUp/Organization/OrganizationForm.tsx
+++ b/frontend/src/app/signUp/Organization/OrganizationForm.tsx
@@ -1,5 +1,5 @@
import { ReactElement, useState } from "react";
-import { Redirect } from "react-router";
+import { Navigate } from "react-router-dom";
import { Card } from "../../commonComponents/Card/Card";
import { CardBackground } from "../../commonComponents/CardBackground/CardBackground";
@@ -109,16 +109,16 @@ const OrganizationForm = () => {
if (orgExternalId) {
return (
-
);
}
diff --git a/frontend/src/app/signUp/SignUpApp.test.tsx b/frontend/src/app/signUp/SignUpApp.test.tsx
index eb2f771857..9e4cd31931 100644
--- a/frontend/src/app/signUp/SignUpApp.test.tsx
+++ b/frontend/src/app/signUp/SignUpApp.test.tsx
@@ -1,5 +1,5 @@
import { render, screen } from "@testing-library/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import SignUpApp from "./SignUpApp";
@@ -7,11 +7,7 @@ describe("SignUpApp", () => {
beforeEach(() => {
render(
-
+
);
});
diff --git a/frontend/src/app/signUp/SignUpApp.tsx b/frontend/src/app/signUp/SignUpApp.tsx
index b8828ec70d..f5290eceae 100644
--- a/frontend/src/app/signUp/SignUpApp.tsx
+++ b/frontend/src/app/signUp/SignUpApp.tsx
@@ -1,15 +1,17 @@
-import { Route, RouteComponentProps } from "react-router-dom";
+import { Route, Routes } from "react-router-dom";
import Page from "../commonComponents/Page/Page";
import Consent from "./IdentityVerification/Consent";
import SignUpGoals from "./Organization/SignUpGoals";
-const SignUpApp: React.FC = ({ match: { path } }) => {
+const SignUpApp = () => {
return (
-
-
+
+ } />
+ } />
+
);
};
diff --git a/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.test.tsx b/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.test.tsx
index f6c65c5f0e..3b517d71ef 100644
--- a/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.test.tsx
+++ b/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.test.tsx
@@ -1,6 +1,6 @@
import { render, screen, waitFor } from "@testing-library/react";
import { MockedProvider } from "@apollo/client/testing";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import {
@@ -52,10 +52,13 @@ const addAdminMutation = {
},
},
};
-
-jest.mock("react-router-dom", () => ({
- Redirect: () => Redirected
,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: () => Redirected
,
+ };
+});
describe("AddOrganizationAdminFormContainer", () => {
describe("loading organizations", () => {
diff --git a/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.tsx b/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.tsx
index 03b88d9fd1..5490b82c84 100644
--- a/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.tsx
+++ b/frontend/src/app/supportAdmin/AddOrganizationAdmin/AddOrganizationAdminFormContainer.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import Alert from "../../commonComponents/Alert";
import { showNotification } from "../../utils";
@@ -59,7 +59,7 @@ const AddOrganizationAdminFormContainer = () => {
};
if (submitted) {
- return ;
+ return ;
}
return (
diff --git a/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.test.tsx b/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.test.tsx
index 9ff01c3c48..288b5837d6 100644
--- a/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.test.tsx
+++ b/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.test.tsx
@@ -32,9 +32,13 @@ jest.mock("../../../generated/graphql", () => {
};
});
-jest.mock("react-router-dom", () => ({
- Redirect: (props: any) => `Redirected to ${props.to}`,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: (props: any) => `Redirected to ${props.to}`,
+ };
+});
describe("DeviceTypeFormContainer", () => {
it("should show the device type form", async () => {
diff --git a/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.tsx b/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.tsx
index 8cd7f39597..478d13c677 100644
--- a/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.tsx
+++ b/frontend/src/app/supportAdmin/DeviceType/DeviceTypeFormContainer.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import {
useCreateDeviceTypeMutation,
@@ -44,7 +44,7 @@ const DeviceTypeFormContainer = () => {
};
if (submitted) {
- return ;
+ return ;
}
if (specimenTypesResults) {
diff --git a/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.test.tsx b/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.test.tsx
index c29f44dd03..fd097688f0 100644
--- a/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.test.tsx
+++ b/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.test.tsx
@@ -78,9 +78,13 @@ jest.mock("../../../generated/graphql", () => {
};
});
-jest.mock("react-router-dom", () => ({
- Redirect: (props: any) => `Redirected to ${props.to}`,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: (props: any) => `Redirected to ${props.to}`,
+ };
+});
describe("ManageDeviceTypeFormContainer", () => {
beforeEach(() => {
diff --git a/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.tsx b/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.tsx
index ce2c1751c2..7fc9c7de8f 100644
--- a/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.tsx
+++ b/frontend/src/app/supportAdmin/DeviceType/ManageDeviceTypeFormContainer.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import {
UpdateDeviceType,
@@ -42,7 +42,7 @@ const ManageDeviceTypeFormContainer = () => {
};
if (submitted) {
- return ;
+ return ;
}
if (deviceTypeResults && specimenTypesResults) {
diff --git a/frontend/src/app/supportAdmin/SupportAdminRoutes.tsx b/frontend/src/app/supportAdmin/SupportAdminRoutes.tsx
index 3af948c757..5c80248fba 100644
--- a/frontend/src/app/supportAdmin/SupportAdminRoutes.tsx
+++ b/frontend/src/app/supportAdmin/SupportAdminRoutes.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Redirect, Route } from "react-router-dom";
+import { Navigate, Route, Routes } from "react-router-dom";
import AddOrganizationAdminFormContainer from "./AddOrganizationAdmin/AddOrganizationAdminFormContainer";
import DeviceTypeFormContainer from "./DeviceType/DeviceTypeFormContainer";
@@ -9,45 +9,34 @@ import PendingOrganizationsContainer from "./PendingOrganizations/PendingOrganiz
import ManageDeviceTypeFormContainer from "./DeviceType/ManageDeviceTypeFormContainer";
interface Props {
- match: { url: string };
isAdmin: boolean;
}
-const SupportAdminRoutes: React.FC = ({ match, isAdmin }) => {
+const SupportAdminRoutes: React.FC = ({ isAdmin }) => {
if (!isAdmin) {
- return (
- (
-
- )}
- />
- );
+ return ;
}
return (
- <>
-
+
}
/>
}
/>
+ } />
}
/>
}
/>
-
- >
+ } />
+
);
};
diff --git a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessForm.test.tsx b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessForm.test.tsx
index f8650b49d3..badd4c8a2f 100644
--- a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessForm.test.tsx
+++ b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessForm.test.tsx
@@ -1,6 +1,6 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { OrganizationOption } from "../Components/OrganizationDropDown";
diff --git a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.test.tsx b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.test.tsx
index 4eef5f0b95..f6c43a62e0 100644
--- a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.test.tsx
+++ b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.test.tsx
@@ -4,7 +4,7 @@ import {
waitForElementToBeRemoved,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { MockedProvider } from "@apollo/client/testing";
import configureStore from "redux-mock-store";
@@ -39,9 +39,13 @@ jest.mock("./TenantDataAccessForm", () => {
);
};
});
-jest.mock("react-router-dom", () => ({
- Redirect: () => Redirected
,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: () => Redirected
,
+ };
+});
const store = configureStore([])({
organization: {
diff --git a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.tsx b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.tsx
index ee2b52fd1f..72cf1797d9 100644
--- a/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.tsx
+++ b/frontend/src/app/supportAdmin/TenantDataAccess/TenantDataAccessFormContainer.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import Alert from "../../commonComponents/Alert";
import { showNotification } from "../../utils";
@@ -54,14 +54,11 @@ const TenantDataAccessFormContainer = () => {
);
showNotification(alert);
setSubmitted(true);
-
- // reload the page, in the future, this should just update state where appropriate
- window.location.reload();
});
};
if (submitted) {
- return ;
+ return ;
}
return (
diff --git a/frontend/src/app/telemetry-provider.tsx b/frontend/src/app/telemetry-provider.tsx
index 2f31dc9bb8..a1cd614443 100644
--- a/frontend/src/app/telemetry-provider.tsx
+++ b/frontend/src/app/telemetry-provider.tsx
@@ -1,16 +1,13 @@
-import React, { Fragment, useEffect } from "react";
-import { useHistory } from "react-router-dom";
+import React, { useEffect } from "react";
import { ai } from "./TelemetryService";
const TelemetryProvider: React.FC = ({ children }) => {
- const history = useHistory();
-
useEffect(() => {
- ai.initialize(history);
- }, [history]);
+ ai.initialize();
+ });
- return {children};
+ return <>{children}>;
};
export default TelemetryProvider;
diff --git a/frontend/src/app/telemetry.test.tsx b/frontend/src/app/telemetry.test.tsx
index 47335904c6..da716aa678 100644
--- a/frontend/src/app/telemetry.test.tsx
+++ b/frontend/src/app/telemetry.test.tsx
@@ -1,9 +1,5 @@
import { SeverityLevel } from "@microsoft/applicationinsights-web";
-import { render } from "@testing-library/react";
-import { MemoryRouter, useHistory } from "react-router-dom";
-import { useEffect } from "react";
-import TelemetryProvider from "./telemetry-provider";
import { ai, getAppInsights, withInsights } from "./TelemetryService";
jest.mock("@microsoft/applicationinsights-web", () => {
@@ -89,26 +85,4 @@ describe("telemetry", () => {
},
});
});
-
- describe("Provider", () => {
- it("provides history object to appInsights", async () => {
- let history: ReturnType | undefined = undefined;
- const GetHistory = () => {
- const h = useHistory();
- useEffect(() => {
- history = h;
- }, [h]);
- return <>>;
- };
-
- render(
-
-
-
-
-
- );
- expect(ai.initialize).toBeCalledWith(history);
- });
- });
});
diff --git a/frontend/src/app/testQueue/QueueItem.test.tsx b/frontend/src/app/testQueue/QueueItem.test.tsx
index ff866515b6..35875cce6d 100644
--- a/frontend/src/app/testQueue/QueueItem.test.tsx
+++ b/frontend/src/app/testQueue/QueueItem.test.tsx
@@ -15,12 +15,14 @@ jest.mock("../TelemetryService", () => ({
getAppInsights: jest.fn(),
}));
-const mockPush = jest.fn();
-jest.mock("react-router-dom", () => ({
- useHistory: () => ({
- push: mockPush,
- }),
-}));
+const mockNavigate = jest.fn();
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ useNavigate: () => mockNavigate,
+ };
+});
const initialDateString = "2021-02-14";
const updatedDateString = "2021-03-10";
@@ -108,7 +110,7 @@ describe("QueueItem", () => {
const patientName = screen.getByText("Potter, Harry James");
expect(patientName).toBeInTheDocument();
userEvent.click(patientName);
- expect(mockPush).toHaveBeenCalledWith({
+ expect(mockNavigate).toHaveBeenCalledWith({
pathname: "/patient/f5c7658d-a0d5-4ec5-a1c9-eafc85fe7554",
search: "?facility=Hogwarts+123",
});
diff --git a/frontend/src/app/testQueue/QueueItem.tsx b/frontend/src/app/testQueue/QueueItem.tsx
index 36a161ea25..2f9f16cc19 100644
--- a/frontend/src/app/testQueue/QueueItem.tsx
+++ b/frontend/src/app/testQueue/QueueItem.tsx
@@ -11,7 +11,7 @@ import Modal from "react-modal";
import classnames from "classnames";
import moment from "moment";
import { useSelector } from "react-redux";
-import { useHistory } from "react-router-dom";
+import { useNavigate } from "react-router-dom";
import Alert from "../commonComponents/Alert";
import Button from "../commonComponents/Button/Button";
@@ -207,7 +207,7 @@ const QueueItem = ({
dateTestedProp,
}: QueueItemProps) => {
const appInsights = getAppInsights();
- const history = useHistory();
+ const navigate = useNavigate();
const trackRemovePatientFromQueue = () => {
if (appInsights) {
@@ -667,7 +667,7 @@ const QueueItem = ({
{
- history.push({
+ navigate({
pathname: `/patient/${patient.internalId}`,
search: `?facility=${facilityId}`,
});
diff --git a/frontend/src/app/testQueue/addToQueue/AddToQueueSearch.tsx b/frontend/src/app/testQueue/addToQueue/AddToQueueSearch.tsx
index 4ae243b463..9423fd64da 100644
--- a/frontend/src/app/testQueue/addToQueue/AddToQueueSearch.tsx
+++ b/frontend/src/app/testQueue/addToQueue/AddToQueueSearch.tsx
@@ -6,7 +6,7 @@ import React, {
useCallback,
} from "react";
import { gql, useMutation, useLazyQuery, useQuery } from "@apollo/client";
-import { useLocation } from "react-router";
+import { useLocation } from "react-router-dom";
import Alert from "../../commonComponents/Alert";
import {
@@ -167,7 +167,7 @@ const AddToQueueSearchBox = ({
}, []);
const { patientId: patientIdParam } =
- useLocation
().state || {};
+ (useLocation().state as StartTestProps) || {};
useQuery<{ patient: Patient }>(QUERY_SINGLE_PATIENT, {
fetchPolicy: "no-cache",
diff --git a/frontend/src/app/testQueue/addToQueue/SearchResults.stories.tsx b/frontend/src/app/testQueue/addToQueue/SearchResults.stories.tsx
index 8f7e434eec..2c2bdc8ead 100644
--- a/frontend/src/app/testQueue/addToQueue/SearchResults.stories.tsx
+++ b/frontend/src/app/testQueue/addToQueue/SearchResults.stories.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { Story, Meta } from "@storybook/react";
import { TestResult } from "../QueueItem";
diff --git a/frontend/src/app/testQueue/addToQueue/SearchResults.test.tsx b/frontend/src/app/testQueue/addToQueue/SearchResults.test.tsx
index 2f68e784aa..3a3384a915 100644
--- a/frontend/src/app/testQueue/addToQueue/SearchResults.test.tsx
+++ b/frontend/src/app/testQueue/addToQueue/SearchResults.test.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
@@ -53,26 +53,35 @@ const patients: Patient[] = [
const mockFacilityID = "facility-id-101";
const RouterWithFacility: React.FC = ({ children }) => (
- {children}
+ {children}
);
-jest.mock("react-router-dom", () => ({
- Redirect: (props: any) => `Redirected to ${props.to}`,
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: (props: any) => `Redirected to ${props.to}`,
+ };
+});
describe("SearchResults", () => {
describe("No Results", () => {
it("should say 'No Results' for no matches", () => {
render(
-
+ }
/>
);
@@ -83,13 +92,18 @@ describe("SearchResults", () => {
it("should show add patient button", () => {
render(
-
+ }
/>
);
@@ -107,13 +121,18 @@ describe("SearchResults", () => {
it("should show matching results", () => {
render(
-
+ }
/>
);
@@ -125,13 +144,18 @@ describe("SearchResults", () => {
const addToQueue = jest.fn();
render(
-
+ }
/>
);
@@ -144,14 +168,19 @@ describe("SearchResults", () => {
const addToQueue = jest.fn();
render(
-
+ }
/>
);
diff --git a/frontend/src/app/testQueue/addToQueue/SearchResults.tsx b/frontend/src/app/testQueue/addToQueue/SearchResults.tsx
index 98d7047532..09b9385639 100644
--- a/frontend/src/app/testQueue/addToQueue/SearchResults.tsx
+++ b/frontend/src/app/testQueue/addToQueue/SearchResults.tsx
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react";
import moment from "moment";
-import { useLocation } from "react-router";
-import { Redirect } from "react-router-dom";
+import { Navigate, useLocation } from "react-router-dom";
import Button from "../../commonComponents/Button/Button";
import AoEModalForm from "../AoEForm/AoEModalForm";
@@ -56,7 +55,7 @@ const SearchResults = (props: QueueProps | TestResultsProps) => {
}, [selectedPatient]);
if (redirect) {
- return ;
+ return ;
}
const actionByPage = (patient: Patient, idx: Number) => {
diff --git a/frontend/src/app/testResults/CleanTestResultsList.test.tsx b/frontend/src/app/testResults/CleanTestResultsList.test.tsx
index 4d8635164f..2c4ed4cd40 100644
--- a/frontend/src/app/testResults/CleanTestResultsList.test.tsx
+++ b/frontend/src/app/testResults/CleanTestResultsList.test.tsx
@@ -1,7 +1,7 @@
import qs from "querystring";
import { render, waitFor } from "@testing-library/react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
@@ -23,27 +23,31 @@ const store = mockStore({
facility: { id: "1", name: "Facility 1" },
});
-const mockPush = jest.fn();
-jest.mock("react-router-dom", () => ({
- useHistory: () => ({
- push: mockPush,
- }),
-}));
+const mockNavigate = jest.fn();
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ useNavigate: () => mockNavigate,
+ };
+});
describe("CleanTestResultsList", () => {
it("should redirect to page 1 of test results", async () => {
await render(
-
-
-
-
-
+
+
+
+ } />
+
+
+
);
await waitFor(() => {
- expect(mockPush).toHaveBeenCalledWith({
+ expect(mockNavigate).toHaveBeenCalledWith({
pathname: "/results/1",
search: qs.stringify({
facility: 1,
diff --git a/frontend/src/app/testResults/CleanTestResultsList.tsx b/frontend/src/app/testResults/CleanTestResultsList.tsx
index 81cf143754..844218b8a3 100644
--- a/frontend/src/app/testResults/CleanTestResultsList.tsx
+++ b/frontend/src/app/testResults/CleanTestResultsList.tsx
@@ -1,24 +1,26 @@
// this component removes all filter search params from the url
import qs from "querystring";
-import { useHistory } from "react-router-dom";
-import React from "react";
+import { useEffect } from "react";
+import { useNavigate } from "react-router-dom";
import { useSelectedFacility } from "../facilitySelect/useSelectedFacility";
const CleanTestResultsList = () => {
- const history = useHistory();
+ const navigate = useNavigate();
const [facility] = useSelectedFacility();
const activeFacilityId = facility?.id || "";
- if (activeFacilityId && history) {
- history.push({
- pathname: "/results/1",
- search: qs.stringify({
- facility: activeFacilityId,
- }),
- });
- }
+ useEffect(() => {
+ if (activeFacilityId && navigate) {
+ navigate({
+ pathname: "/results/1",
+ search: qs.stringify({
+ facility: activeFacilityId,
+ }),
+ });
+ }
+ }, [activeFacilityId, navigate]);
return <>>;
};
diff --git a/frontend/src/app/testResults/TestResultsList.test.tsx b/frontend/src/app/testResults/TestResultsList.test.tsx
index bb783f0af3..785e23876a 100644
--- a/frontend/src/app/testResults/TestResultsList.test.tsx
+++ b/frontend/src/app/testResults/TestResultsList.test.tsx
@@ -8,7 +8,7 @@ import {
within,
} from "@testing-library/react";
import { Provider } from "react-redux";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import userEvent from "@testing-library/user-event";
@@ -1000,7 +1000,7 @@ describe("TestResultsList", () => {
>
-
+
@@ -1047,7 +1047,7 @@ describe("TestResultsList", () => {
-
+
@@ -1332,7 +1332,7 @@ describe("TestResultsList", () => {
-
+
diff --git a/frontend/src/app/testResults/TestResultsList.tsx b/frontend/src/app/testResults/TestResultsList.tsx
index 6de16594e3..e67a253a96 100644
--- a/frontend/src/app/testResults/TestResultsList.tsx
+++ b/frontend/src/app/testResults/TestResultsList.tsx
@@ -1,6 +1,6 @@
import qs from "querystring";
-import { useHistory } from "react-router-dom";
+import { useLocation, useNavigate, useParams } from "react-router-dom";
import { gql, useLazyQuery, useQuery } from "@apollo/client";
import React, {
ChangeEventHandler,
@@ -629,10 +629,6 @@ export const testResultQuery = gql`
}
`;
-type TestResultsListProps = {
- pageNumber: number;
-};
-
export interface ResultsQueryVariables {
patientId?: string | null;
facilityId: string;
@@ -644,19 +640,21 @@ export interface ResultsQueryVariables {
pageSize: number;
}
-const TestResultsList = (props: TestResultsListProps) => {
+const TestResultsList = () => {
useDocumentTitle("Results");
+ const urlParams = useParams();
const [facility] = useSelectedFacility();
const activeFacilityId = facility?.id || "";
- const history = useHistory();
+ const navigate = useNavigate();
+ const location = useLocation();
- const patientId = getParameterFromUrl("patientId", history.location);
- const startDate = getParameterFromUrl("startDate", history.location);
- const endDate = getParameterFromUrl("endDate", history.location);
- const role = getParameterFromUrl("role", history.location);
- const result = getParameterFromUrl("result", history.location);
+ const patientId = getParameterFromUrl("patientId", location);
+ const startDate = getParameterFromUrl("startDate", location);
+ const endDate = getParameterFromUrl("endDate", location);
+ const role = getParameterFromUrl("role", location);
+ const result = getParameterFromUrl("result", location);
const filterParams: FilterParams = {
...(patientId && { patientId: patientId }),
@@ -667,7 +665,7 @@ const TestResultsList = (props: TestResultsListProps) => {
};
const filter = (params: FilterParams) => {
- history.push({
+ navigate({
pathname: "/results/1",
search: qs.stringify({
facility: activeFacilityId,
@@ -681,16 +679,16 @@ const TestResultsList = (props: TestResultsListProps) => {
filter({ [key]: val });
};
- const refetch = () => history.go(0);
+ const refetch = () => navigate(0);
const clearFilterParams = () =>
- history.push({
+ navigate({
pathname: "/results/1",
search: qs.stringify({ facility: activeFacilityId }),
});
const entriesPerPage = 20;
- const pageNumber = props.pageNumber || 1;
+ const pageNumber = Number(urlParams.pageNumber) || 1;
const resultsQueryVariables: ResultsQueryVariables = {
facilityId: activeFacilityId,
diff --git a/frontend/src/app/utils/Prompt.test.tsx b/frontend/src/app/utils/Prompt.test.tsx
new file mode 100644
index 0000000000..6f2e3b6c42
--- /dev/null
+++ b/frontend/src/app/utils/Prompt.test.tsx
@@ -0,0 +1,146 @@
+import { Link, MemoryRouter, Outlet, Route, Routes } from "react-router-dom";
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+
+import Prompt from "./Prompt";
+
+describe("A ", () => {
+ it("calls window.confirm with the prompt message", () => {
+ const confirmMock = jest
+ .spyOn(window, "confirm")
+ .mockImplementation((_message) => false);
+
+ render(
+
+
+
+
+
+ >
+ }
+ >
+
+ This is the first page
+ Go to a new page
+ >
+ }
+ />
+ Went to a new page! }
+ />
+
+
+
+ );
+ expect(screen.getByText("This is the first page")).toBeInTheDocument();
+
+ // WHEN
+ userEvent.click(screen.getByText("Go to a new page"));
+
+ // THEN
+ expect(screen.queryByText("Went to a new page!")).not.toBeInTheDocument();
+
+ expect(confirmMock).toHaveBeenCalledWith(
+ expect.stringMatching("Are you sure?")
+ );
+ });
+
+ it("calls window.confirm with the prompt message, and redirects when confirmed", () => {
+ const confirmMock = jest
+ .spyOn(window, "confirm")
+ .mockImplementation((_message) => true);
+
+ render(
+
+
+
+
+
+ >
+ }
+ >
+
+ This is the first page
+ Go to a new page
+ >
+ }
+ />
+ Went to a new page! }
+ />
+
+
+
+ );
+ expect(screen.getByText("This is the first page")).toBeInTheDocument();
+
+ // WHEN
+ userEvent.click(screen.getByText("Go to a new page"));
+
+ // THEN
+ expect(confirmMock).toHaveBeenCalledWith(
+ expect.stringMatching("Are you sure?")
+ );
+
+ expect(screen.getByText("Went to a new page!")).toBeInTheDocument();
+ });
+
+ it("does not call window.confirm with when=false", () => {
+ const confirmMock = jest
+ .spyOn(window, "confirm")
+ .mockImplementation((_message) => true);
+
+ render(
+
+
+
+
+
+ >
+ }
+ >
+
+ This is the first page
+ Go to a new page
+ >
+ }
+ />
+ Went to a new page!}
+ />
+
+
+
+ );
+ expect(screen.getByText("This is the first page")).toBeInTheDocument();
+
+ // WHEN
+ userEvent.click(screen.getByText("Go to a new page"));
+
+ // THEN
+ expect(screen.getByText("Went to a new page!")).toBeInTheDocument();
+
+ expect(confirmMock).not.toHaveBeenCalled();
+ });
+});
diff --git a/frontend/src/app/utils/Prompt.tsx b/frontend/src/app/utils/Prompt.tsx
new file mode 100644
index 0000000000..ed5bef5048
--- /dev/null
+++ b/frontend/src/app/utils/Prompt.tsx
@@ -0,0 +1,76 @@
+// Reimplementation of and usePrompt() from react-router-dom since they were removed for v6.
+// Remove this once react-router readds these at some point.
+// Credit to @code-jongleur on GitHub: https://github.com/remix-run/react-router/issues/8139#issuecomment-1014746446
+
+import { useContext, useEffect, useCallback } from "react";
+import { UNSAFE_NavigationContext as NavigationContext } from "react-router-dom";
+import type { History } from "history";
+
+declare type Navigator = Pick<
+ History,
+ "go" | "push" | "replace" | "createHref" | "block"
+>;
+
+/**
+ * Blocks all navigation attempts. This is useful for preventing the page from
+ * changing until some condition is met, like saving form data.
+ *
+ * @param blocker
+ * @param when
+ * @see https://reactrouter.com/api/useBlocker
+ */
+export function useBlocker(blocker: any, when = true) {
+ const { navigator } = useContext(NavigationContext);
+
+ useEffect(() => {
+ if (!when) {
+ return;
+ }
+
+ const unblock = (navigator as Navigator).block((tx: any) => {
+ const autoUnblockingTx = {
+ ...tx,
+ retry() {
+ unblock();
+ tx.retry();
+ },
+ };
+
+ blocker(autoUnblockingTx);
+ });
+
+ return unblock;
+ }, [navigator, blocker, when]);
+}
+/**
+ * Prompts the user with an Alert before they leave the current screen.
+ *
+ * @param message
+ * @param when
+ */
+export function usePrompt(message: string, when = true) {
+ const blocker = useCallback(
+ (tx) => {
+ // eslint-disable-next-line no-alert
+ if (window.confirm(message)) {
+ tx.retry();
+ }
+ },
+ [message]
+ );
+
+ useBlocker(blocker, when);
+}
+
+interface Props {
+ message: string;
+ when: boolean;
+}
+
+const Prompt = ({ message, when }: Props) => {
+ usePrompt(message, when);
+
+ return null;
+};
+
+export default Prompt;
diff --git a/frontend/src/app/utils/url.ts b/frontend/src/app/utils/url.ts
index 608d84c208..4e4f605e13 100644
--- a/frontend/src/app/utils/url.ts
+++ b/frontend/src/app/utils/url.ts
@@ -2,7 +2,7 @@ import { Location } from "history";
export const getParameterFromUrl = (
param: string,
- location?: Location
+ location?: Location
): string | null => {
const queryParams = new URLSearchParams(
location ? location.search : window.location.search
@@ -10,9 +10,8 @@ export const getParameterFromUrl = (
return queryParams.has(param) ? queryParams.get(param) : null;
};
-export const getFacilityIdFromUrl = (
- location?: Location
-): string | null => getParameterFromUrl("facility", location);
+export const getFacilityIdFromUrl = (location?: Location): string | null =>
+ getParameterFromUrl("facility", location);
export const getPatientLinkIdFromUrl = (): string | null =>
getParameterFromUrl("plid");
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index d518ea6b68..190d7cb775 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -8,7 +8,12 @@ import {
InMemoryCache,
concat,
} from "@apollo/client";
-import { Switch, Route, BrowserRouter as Router } from "react-router-dom";
+import {
+ Route,
+ BrowserRouter as Router,
+ Routes,
+ Navigate,
+} from "react-router-dom";
import { createUploadLink } from "apollo-upload-client";
import { ErrorResponse, onError } from "@apollo/client/link/error";
import { ApplicationInsights } from "@microsoft/applicationinsights-web";
@@ -117,19 +122,20 @@ export const ReactApp = (
-
-
-
-
-
+
+ } />
+ } />
+ } />
+ } />
}
/>
-
-
- <>Page not found>} />
-
+ } />
+ } />
+ } />
+ Page not found>} />
+
diff --git a/frontend/src/patientApp/GuardedRoute.test.tsx b/frontend/src/patientApp/GuardedRoute.test.tsx
index 9ed3f81fe3..3fce943a39 100644
--- a/frontend/src/patientApp/GuardedRoute.test.tsx
+++ b/frontend/src/patientApp/GuardedRoute.test.tsx
@@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { MemoryRouter, Route } from "react-router";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
import GuardedRoute from "./GuardedRoute";
import TermsOfService from "./timeOfTest/TermsOfService";
@@ -12,18 +12,17 @@ const store = mockStore({
plid: "foo",
});
const mockContainer = (auth: boolean) => (
-
-
-
-
- This is some very specific text
-
-
-
+
+
+
+ } />}
+ path="/terms-of-service"
+ />
+ This is some very specific text} />
+
+
+
);
describe("GuardedRoute", () => {
diff --git a/frontend/src/patientApp/GuardedRoute.tsx b/frontend/src/patientApp/GuardedRoute.tsx
index 10943ea179..4f9ba6db1b 100644
--- a/frontend/src/patientApp/GuardedRoute.tsx
+++ b/frontend/src/patientApp/GuardedRoute.tsx
@@ -1,25 +1,27 @@
import { useSelector } from "react-redux";
-import { Route, Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
-const GuardedRoute = ({ component: Component, auth, ...rest }: any) => {
+interface Props {
+ auth: boolean;
+ element: any;
+}
+
+const GuardedRoute = ({ auth, element }: Props) => {
const plid = useSelector((state: any) => state.plid);
- return (
-
- auth === true ? (
-
- ) : (
-
- )
- }
- />
- );
+
+ if (auth) {
+ return element;
+ } else {
+ return (
+
+ );
+ }
};
export default GuardedRoute;
diff --git a/frontend/src/patientApp/PatientApp.tsx b/frontend/src/patientApp/PatientApp.tsx
index 66d51b0f14..229ba20a05 100644
--- a/frontend/src/patientApp/PatientApp.tsx
+++ b/frontend/src/patientApp/PatientApp.tsx
@@ -1,14 +1,14 @@
import { FunctionComponent, useEffect } from "react";
import { useDispatch, connect, useSelector } from "react-redux";
-import { Route, Switch, BrowserRouter as Router } from "react-router-dom";
+import { Route, Routes } from "react-router-dom";
import { validate as isValidUUID } from "uuid";
import { useTranslation } from "react-i18next";
import Page from "../app/commonComponents/Page/Page";
import { setInitialState } from "../app/store";
-import { getPatientLinkIdFromUrl } from "../app/utils/url";
import PageNotFound from "../app/commonComponents/PageNotFound";
import Alert from "../app/commonComponents/Alert";
+import { getPatientLinkIdFromUrl } from "../app/utils/url";
import PatientHeader from "./PatientHeader";
import TermsOfService from "./timeOfTest/TermsOfService";
@@ -73,38 +73,37 @@ const PatientApp = () => {
-
-
-
-
-
-
-
-
-
-
-
-
+
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ } />
+ }
+ />
+ } />
+ }
+ />
+ } />}
+ />
+ } />}
+ />
+
);
diff --git a/frontend/src/patientApp/PxpApiService.ts b/frontend/src/patientApp/PxpApiService.ts
index 9221c28c05..2f7b42d242 100644
--- a/frontend/src/patientApp/PxpApiService.ts
+++ b/frontend/src/patientApp/PxpApiService.ts
@@ -36,7 +36,7 @@ export type SelfRegistrationData = Omit<
"facilityId" | "address"
> & {
birthDate: ISODate;
- registrationLink: string;
+ registrationLink: string | undefined;
address: Omit & {
postalCode: string | null;
};
@@ -77,7 +77,9 @@ export class PxpApi {
});
}
- static getEntityName = async (registrationLink: string): Promise => {
+ static getEntityName = async (
+ registrationLink: string | undefined
+ ): Promise => {
return api.getRequest(
`/register/entity-name?patientRegistrationLink=${registrationLink}`
);
@@ -91,7 +93,7 @@ export class PxpApi {
firstName: string;
lastName: string;
birthDate: ISODate;
- registrationLink: string;
+ registrationLink: string | undefined;
}): Promise => {
const { registrationLink, ...body } = person;
diff --git a/frontend/src/patientApp/selfRegistration/RegistrationContainer.tsx b/frontend/src/patientApp/selfRegistration/RegistrationContainer.tsx
index a2a81f8bb9..87d0c8f239 100644
--- a/frontend/src/patientApp/selfRegistration/RegistrationContainer.tsx
+++ b/frontend/src/patientApp/selfRegistration/RegistrationContainer.tsx
@@ -4,7 +4,7 @@ import PageNotFound from "../../app/commonComponents/PageNotFound";
import { PxpApi } from "../PxpApiService";
type Props = {
- registrationLink: string;
+ registrationLink: string | undefined;
setEntityName: (name: string) => void;
};
diff --git a/frontend/src/patientApp/selfRegistration/SelfRegistration.test.tsx b/frontend/src/patientApp/selfRegistration/SelfRegistration.test.tsx
index 218046a759..73bb8ef0f3 100644
--- a/frontend/src/patientApp/selfRegistration/SelfRegistration.test.tsx
+++ b/frontend/src/patientApp/selfRegistration/SelfRegistration.test.tsx
@@ -5,7 +5,7 @@ import {
waitForElementToBeRemoved,
} from "@testing-library/react";
import { Provider } from "react-redux";
-import { MemoryRouter, Route } from "react-router";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
import createMockStore from "redux-mock-store";
import faker from "faker";
import "../../i18n";
@@ -54,11 +54,12 @@ describe("SelfRegistration", () => {
render(
-
+
+ }
+ />
+
);
@@ -72,11 +73,12 @@ describe("SelfRegistration", () => {
render(
-
+
+ }
+ />
+
);
@@ -107,11 +109,12 @@ describe("SelfRegistration", () => {
render(
-
+
+ }
+ />
+
);
diff --git a/frontend/src/patientApp/selfRegistration/SelfRegistration.tsx b/frontend/src/patientApp/selfRegistration/SelfRegistration.tsx
index a218d7ab22..73609ec4f2 100644
--- a/frontend/src/patientApp/selfRegistration/SelfRegistration.tsx
+++ b/frontend/src/patientApp/selfRegistration/SelfRegistration.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { useParams } from "react-router";
+import { useParams } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import moment from "moment";
import "react-toastify/dist/ReactToastify.css";
@@ -23,7 +23,9 @@ enum RegistrationStep {
}
export const SelfRegistration = () => {
- const { registrationLink } = useParams<{ registrationLink: string }>();
+ const { registrationLink } = useParams<{
+ registrationLink: string | undefined;
+ }>();
const [step, setStep] = useState(RegistrationStep.TERMS);
const [entityName, setEntityName] = useState("");
const [personName, setPersonName] = useState("");
diff --git a/frontend/src/patientApp/selfRegistration/SelfRegistrationForm.tsx b/frontend/src/patientApp/selfRegistration/SelfRegistrationForm.tsx
index 78398f6d02..b9595f04af 100644
--- a/frontend/src/patientApp/selfRegistration/SelfRegistrationForm.tsx
+++ b/frontend/src/patientApp/selfRegistration/SelfRegistrationForm.tsx
@@ -17,7 +17,7 @@ type Props = {
savePerson: (data: any) => void;
onDuplicate: (person: Pick) => void;
entityName: string;
- registrationLink: string;
+ registrationLink: string | undefined;
};
export const SelfRegistrationForm = ({
diff --git a/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.test.tsx b/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.test.tsx
index a64f1a3dd6..f84cc59e55 100644
--- a/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.test.tsx
@@ -5,13 +5,13 @@ import configureStore from "redux-mock-store";
import AoEPatientFormContainer from "./AoEPatientFormContainer";
-jest.mock("react-router-dom", () => ({
- useHistory: () => ({
- listen: jest.fn(),
- push: jest.fn(),
- }),
-}));
-
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ useNavigate: jest.fn(),
+ };
+});
window.scrollTo = jest.fn();
describe("AoEPatientFormContainer", () => {
diff --git a/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.tsx b/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.tsx
index 87f70c1835..5b21a49b2c 100644
--- a/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.tsx
+++ b/frontend/src/patientApp/timeOfTest/AoEPatientFormContainer.tsx
@@ -1,12 +1,12 @@
import React, { useEffect, useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { connect, useSelector } from "react-redux";
import AoEForm from "../../app/testQueue/AoEForm/AoEForm";
import { showError } from "../../app/utils";
-import { getPatientLinkIdFromUrl } from "../../app/utils/url";
import PatientTimeOfTestContainer from "../PatientTimeOfTestContainer";
import { PxpApi } from "../PxpApiService";
+import { getPatientLinkIdFromUrl } from "../../app/utils/url";
const AoEPatientFormContainer: React.FC = () => {
const [nextPage, setNextPage] = useState(false);
@@ -30,9 +30,9 @@ const AoEPatientFormContainer: React.FC = () => {
if (nextPage) {
return (
-
diff --git a/frontend/src/patientApp/timeOfTest/DOB.tsx b/frontend/src/patientApp/timeOfTest/DOB.tsx
index 0fdf5d01d2..b948b547a9 100644
--- a/frontend/src/patientApp/timeOfTest/DOB.tsx
+++ b/frontend/src/patientApp/timeOfTest/DOB.tsx
@@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from "react";
import { connect, useDispatch, useSelector } from "react-redux";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import moment from "moment";
import { useTranslation } from "react-i18next";
import { validate as isValidUUID } from "uuid";
@@ -97,18 +97,18 @@ const DOB = () => {
}
if (patient?.orderStatus === "COMPLETED") {
return (
-
);
} else if (patient?.firstName) {
return (
-
diff --git a/frontend/src/patientApp/timeOfTest/PatientFormContainer.test.tsx b/frontend/src/patientApp/timeOfTest/PatientFormContainer.test.tsx
index b251689931..f85dbf6154 100644
--- a/frontend/src/patientApp/timeOfTest/PatientFormContainer.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientFormContainer.test.tsx
@@ -11,13 +11,15 @@ jest.mock("@trussworks/react-uswds", () => ({
}));
const mockStore = configureStore([]);
-jest.mock("react-router-dom", () => ({
- Prompt: (_props: any) => <>>,
- useHistory: () => ({
- listen: jest.fn(),
- push: jest.fn(),
- }),
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ useNavigate: jest.fn(),
+ };
+});
+
+jest.mock("../../app/utils/Prompt", () => (_props: any) => null);
describe("PatientFormContainer", () => {
it("renders", () => {
diff --git a/frontend/src/patientApp/timeOfTest/PatientFormContainer.tsx b/frontend/src/patientApp/timeOfTest/PatientFormContainer.tsx
index 315c191696..5d6fdb3a70 100644
--- a/frontend/src/patientApp/timeOfTest/PatientFormContainer.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientFormContainer.tsx
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { connect, useSelector, useDispatch } from "react-redux";
-import { Redirect, useHistory } from "react-router-dom";
+import { Navigate, useNavigate } from "react-router-dom";
import { setPatient as reduxSetPatient } from "../../app/store";
import { PxpApi } from "../../patientApp/PxpApiService";
@@ -13,7 +13,7 @@ import Alert from "../../app/commonComponents/Alert";
import Button from "../../app/commonComponents/Button/Button";
const PatientFormContainer = () => {
- const history = useHistory();
+ const navigate = useNavigate();
const [nextPage, setNextPage] = useState(false);
const patient = useSelector((state: any) => state.patient);
@@ -28,9 +28,9 @@ const PatientFormContainer = () => {
if (nextPage) {
return (
-
@@ -100,7 +100,7 @@ const PatientFormContainer = () => {
className="margin-top-1 mobile-lg:margin-top-0 margin-right-0"
variant="outline"
label={"Back"}
- onClick={() => history.goBack()}
+ onClick={() => navigate(-1)}
/>
)}
diff --git a/frontend/src/patientApp/timeOfTest/PatientProfile.test.tsx b/frontend/src/patientApp/timeOfTest/PatientProfile.test.tsx
index 38b4d0ea0b..475df36c6d 100644
--- a/frontend/src/patientApp/timeOfTest/PatientProfile.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientProfile.test.tsx
@@ -1,18 +1,18 @@
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { MemoryRouter } from "react-router";
import { render, screen } from "@testing-library/react";
import PatientProfile from "./PatientProfile";
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ Navigate: () => Redirected
,
+ };
+});
+
const mockStore = configureStore([]);
-const mockContainer = (store: any, patient: any) => (
-
-
-
-
-
-);
describe("PatientProfile", () => {
it("renders", () => {
@@ -24,7 +24,11 @@ describe("PatientProfile", () => {
const store = mockStore({
plid: "foo",
});
- render(mockContainer(store, patient));
+ render(
+
+
+
+ );
expect(
screen.getByText("General information", { exact: false })
).toBeInTheDocument();
@@ -33,8 +37,14 @@ describe("PatientProfile", () => {
const store = mockStore({
plid: "foo",
});
- render(mockContainer(store, null));
+ render(
+
+
+
+ );
// eslint-disable-next-line no-restricted-globals
- expect(location.pathname).toEqual("/");
+ expect(
+ screen.getByText("Redirected", { exact: false })
+ ).toBeInTheDocument();
});
});
diff --git a/frontend/src/patientApp/timeOfTest/PatientProfile.tsx b/frontend/src/patientApp/timeOfTest/PatientProfile.tsx
index 6e6a1867ef..2e9d5eeb90 100644
--- a/frontend/src/patientApp/timeOfTest/PatientProfile.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientProfile.tsx
@@ -1,5 +1,5 @@
import moment from "moment";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { formatFullName } from "../../app/utils/user";
@@ -19,9 +19,9 @@ const PatientProfile = ({ patient }: Props) => {
const plid = useSelector((state: any) => state.plid);
if (!patient) {
return (
-
diff --git a/frontend/src/patientApp/timeOfTest/PatientProfileContainer.test.tsx b/frontend/src/patientApp/timeOfTest/PatientProfileContainer.test.tsx
index 7e42366dfb..5ca1ce3218 100644
--- a/frontend/src/patientApp/timeOfTest/PatientProfileContainer.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientProfileContainer.test.tsx
@@ -7,12 +7,13 @@ import PatientProfileContainer from "./PatientProfileContainer";
const mockStore = configureStore([]);
-jest.mock("react-router-dom", () => ({
- useHistory: () => ({
- listen: jest.fn(),
- push: jest.fn(),
- }),
-}));
+jest.mock("react-router-dom", () => {
+ const original = jest.requireActual("react-router-dom");
+ return {
+ ...original,
+ useNavigate: jest.fn(),
+ };
+});
describe("PatientProfileContainer", () => {
it("renders", () => {
diff --git a/frontend/src/patientApp/timeOfTest/PatientProfileContainer.tsx b/frontend/src/patientApp/timeOfTest/PatientProfileContainer.tsx
index f0bc86bc2e..c185d8bf91 100644
--- a/frontend/src/patientApp/timeOfTest/PatientProfileContainer.tsx
+++ b/frontend/src/patientApp/timeOfTest/PatientProfileContainer.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import { connect, useSelector } from "react-redux";
import Button from "../../app/commonComponents/Button/Button";
@@ -15,10 +15,9 @@ const PatientProfileContainer = () => {
if (editPage) {
return (
-
@@ -27,9 +26,9 @@ const PatientProfileContainer = () => {
if (nextPage) {
return (
-
diff --git a/frontend/src/patientApp/timeOfTest/TermsOfService.test.tsx b/frontend/src/patientApp/timeOfTest/TermsOfService.test.tsx
index 276b71b4da..61fe094958 100644
--- a/frontend/src/patientApp/timeOfTest/TermsOfService.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/TermsOfService.test.tsx
@@ -2,8 +2,6 @@ import { MockedProvider } from "@apollo/client/testing";
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { Router } from "react-router-dom";
-import { createMemoryHistory } from "history";
import TermsOfService from "./TermsOfService";
@@ -17,13 +15,11 @@ describe("TermsOfService", () => {
plid: "foo",
});
render(
-
-
-
-
-
-
-
+
+
+
+
+
);
expect(screen.getByText("Terms of service")).toBeInTheDocument();
diff --git a/frontend/src/patientApp/timeOfTest/TermsOfService.tsx b/frontend/src/patientApp/timeOfTest/TermsOfService.tsx
index c29b28584b..ac986b8e30 100644
--- a/frontend/src/patientApp/timeOfTest/TermsOfService.tsx
+++ b/frontend/src/patientApp/timeOfTest/TermsOfService.tsx
@@ -1,8 +1,8 @@
import React, { useState } from "react";
import { useSelector } from "react-redux";
-import { Redirect } from "react-router";
import classnames from "classnames";
import { Trans, useTranslation } from "react-i18next";
+import { Navigate } from "react-router-dom";
import Button from "../../app/commonComponents/Button/Button";
@@ -24,9 +24,9 @@ const TermsOfService: React.FunctionComponent = ({
if (nextPage) {
return (
-
diff --git a/frontend/src/patientApp/timeOfTest/TestResult.test.tsx b/frontend/src/patientApp/timeOfTest/TestResult.test.tsx
index 783c960563..f5a36200b5 100644
--- a/frontend/src/patientApp/timeOfTest/TestResult.test.tsx
+++ b/frontend/src/patientApp/timeOfTest/TestResult.test.tsx
@@ -1,7 +1,5 @@
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
-import { Router } from "react-router-dom";
-import { createMemoryHistory } from "history";
import { render, screen } from "@testing-library/react";
import TestResult from "./TestResult";
@@ -27,11 +25,9 @@ describe("TestResult", () => {
it("should show the patient/device name", () => {
const store = mockStore(getPatientLinkData("UNDETERMINED"));
render(
-
-
-
-
-
+
+
+
);
expect(screen.getByText("SARS-CoV-2 result")).toBeInTheDocument();
@@ -42,11 +38,9 @@ describe("TestResult", () => {
it("should show a positive result", () => {
const store = mockStore(getPatientLinkData("POSITIVE"));
render(
-
-
-
-
-
+
+
+
);
expect(screen.getByText("SARS-CoV-2 result")).toBeInTheDocument();
@@ -55,11 +49,9 @@ describe("TestResult", () => {
it("should show a negative result", () => {
const store = mockStore(getPatientLinkData("NEGATIVE"));
render(
-
-
-
-
-
+
+
+
);
expect(screen.getByText("SARS-CoV-2 result")).toBeInTheDocument();
@@ -68,11 +60,9 @@ describe("TestResult", () => {
it("should show an inconclusive result", () => {
const store = mockStore(getPatientLinkData("UNDETERMINED"));
render(
-
-
-
-
-
+
+
+
);
expect(screen.getByText("SARS-CoV-2 result")).toBeInTheDocument();
diff --git a/frontend/src/scss/PrimeStyles.scss b/frontend/src/scss/PrimeStyles.scss
index e671d305ec..567ad782d4 100644
--- a/frontend/src/scss/PrimeStyles.scss
+++ b/frontend/src/scss/PrimeStyles.scss
@@ -713,6 +713,7 @@ $results-dropdown-spacing: #{units(4)} - #{units(2)} - 22px - #{units(4)}; // he
.active-nav-item {
background-color: color($theme-color-primary-darker);
+ color: white !important;
}
.prime-nav-close-button {
diff --git a/frontend/src/stories/OrderingProviderList.stories.tsx b/frontend/src/stories/OrderingProviderList.stories.tsx
index 88fe638f06..3b6e839756 100644
--- a/frontend/src/stories/OrderingProviderList.stories.tsx
+++ b/frontend/src/stories/OrderingProviderList.stories.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { MemoryRouter } from "react-router";
+import { MemoryRouter } from "react-router-dom";
import { Story, Meta } from "@storybook/react";
import OrderingProviderList, {
diff --git a/frontend/src/stories/testQueue/QueueItem.stories.tsx b/frontend/src/stories/testQueue/QueueItem.stories.tsx
index cee3d16d3c..108e3504d6 100644
--- a/frontend/src/stories/testQueue/QueueItem.stories.tsx
+++ b/frontend/src/stories/testQueue/QueueItem.stories.tsx
@@ -1,6 +1,7 @@
import { Story, Meta } from "@storybook/react";
import { uniqueId } from "lodash";
import { Provider } from "react-redux";
+import { MemoryRouter } from "react-router-dom";
import createMockStore from "redux-mock-store";
import QueueItem, { QueueItemProps } from "../../app/testQueue/QueueItem";
@@ -39,9 +40,11 @@ const store = mockStore({
const Template: Story = (args) => {
return (
-
- ;
-
+
+
+ ;
+
+
);
};
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 8c49375f98..e008525ae5 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -3061,7 +3061,14 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.9.2":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
+ integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
@@ -3075,13 +3082,6 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.9.2":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
- integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
- dependencies:
- regenerator-runtime "^0.13.4"
-
"@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.3.3":
version "7.12.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc"
@@ -6076,6 +6076,11 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.10.tgz#992865313bb97d80eefa6aab5f2e4f1e10e84e86"
integrity sha512-kq1vceWANyZLEt/+hbTWSAjLNhhXYgUw6Ywi0KQ9C7pZJP4Qrr0xjSKb3t59e5GwWtk1L6zt5KTxjH4oPk2l/w==
+"@types/history@^4.7.11":
+ version "4.7.11"
+ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
+ integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
+
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
@@ -6359,12 +6364,12 @@
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
-"@types/react-router-dom@^5.1.6":
- version "5.1.6"
- resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.6.tgz#07b14e7ab1893a837c8565634960dc398564b1fb"
- integrity sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==
+"@types/react-router-dom@^5.3.3":
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
+ integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
dependencies:
- "@types/history" "*"
+ "@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "*"
@@ -12696,7 +12701,7 @@ history@5.0.0:
dependencies:
"@babel/runtime" "^7.7.6"
-history@^4.10.1, history@^4.9.0:
+history@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
@@ -12724,7 +12729,7 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -13780,11 +13785,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0:
dependencies:
is-docker "^2.0.0"
-isarray@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
- integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
-
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -15065,7 +15065,7 @@ longest-streak@^2.0.0:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
-loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -15513,14 +15513,6 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-mini-create-react-context@^0.4.0:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
- integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
- dependencies:
- "@babel/runtime" "^7.12.1"
- tiny-warning "^1.0.3"
-
mini-css-extract-plugin@^2.4.5:
version "2.4.6"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.6.tgz#0f925aaa02dd26513bac40802062a87ebe32e7cc"
@@ -16648,13 +16640,6 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
-path-to-regexp@^1.7.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
- integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
- dependencies:
- isarray "0.0.1"
-
path-to-regexp@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
@@ -17613,7 +17598,7 @@ prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -17622,6 +17607,15 @@ prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1,
object-assign "^4.1.1"
react-is "^16.8.1"
+prop-types@^15.6.2:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
property-expr@^2.0.4:
version "2.0.4"
resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
@@ -18008,7 +18002,7 @@ react-is@17.0.2, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
+react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -18072,20 +18066,7 @@ react-refresh@^0.11.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
-react-router-dom@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
- integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
- dependencies:
- "@babel/runtime" "^7.1.2"
- history "^4.9.0"
- loose-envify "^1.3.1"
- prop-types "^15.6.2"
- react-router "5.2.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
-react-router-dom@^6.0.0:
+react-router-dom@^6.0.0, react-router-dom@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.1.tgz#32ec81829152fbb8a7b045bf593a22eadf019bec"
integrity sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==
@@ -18093,22 +18074,6 @@ react-router-dom@^6.0.0:
history "^5.2.0"
react-router "6.2.1"
-react-router@5.2.0, react-router@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
- integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
- dependencies:
- "@babel/runtime" "^7.1.2"
- history "^4.9.0"
- hoist-non-react-statics "^3.1.0"
- loose-envify "^1.3.1"
- mini-create-react-context "^0.4.0"
- path-to-regexp "^1.7.0"
- prop-types "^15.6.2"
- react-is "^16.6.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
react-router@6.2.1, react-router@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.2.1.tgz#be2a97a6006ce1d9123c28934e604faef51448a3"
@@ -18379,16 +18344,16 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
-regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
- version "0.13.7"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
- integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
-
-regenerator-runtime@^0.13.9:
+regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.9:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+regenerator-runtime@^0.13.7:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regenerator-transform@^0.14.2:
version "0.14.5"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"
@@ -20444,11 +20409,11 @@ tiny-emitter@^2.0.0:
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tiny-invariant@^1.0.2:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
- integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
+ integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
-tiny-warning@^1.0.0, tiny-warning@^1.0.3:
+tiny-warning@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==