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 db80eae099..4a05f5c0a7 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..9d136da742 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,127 @@ 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/FacilityForm.test.tsx b/frontend/src/app/Settings/Facility/FacilityForm.test.tsx similarity index 98% rename from frontend/src/app/Settings/FacilityForm.test.tsx rename to frontend/src/app/Settings/Facility/FacilityForm.test.tsx index 624ed120b9..f8d23bbe0b 100644 --- a/frontend/src/app/Settings/FacilityForm.test.tsx +++ b/frontend/src/app/Settings/Facility/FacilityForm.test.tsx @@ -1,15 +1,15 @@ 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"; -import * as state from "../utils/state"; -import * as smartyStreets from "../utils/smartyStreets"; +import * as clia from "../../utils/clia"; +import * as state from "../../utils/state"; +import * as smartyStreets from "../../utils/smartyStreets"; -import FacilityForm from "./Facility/FacilityForm"; +import FacilityForm from "./FacilityForm"; -import "../../i18n"; +import "../../../i18n"; let saveFacility: jest.Mock; 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..acdf0b64c0 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, Route, Routes } 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(), })); @@ -182,13 +185,18 @@ describe("FacilityFormContainer", () => { trackEvent: trackEventMock, })); render( - - - - - - - + + + + + } + /> + + + + ); }); diff --git a/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx b/frontend/src/app/Settings/Facility/FacilityFormContainer.tsx index 324df87801..7701ac98b1 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, useParams } from "react-router-dom"; import { useDispatch } from "react-redux"; import { updateFacility } from "../../store"; @@ -158,11 +158,11 @@ export const ADD_FACILITY_MUTATION = gql` `; interface Props { - facilityId: string; newOrg?: boolean; } const FacilityFormContainer: any = (props: Props) => { + const { facilityId } = useParams(); const { data, loading, error } = useQuery( GET_FACILITY_QUERY, { @@ -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) => { @@ -199,12 +199,12 @@ const FacilityFormContainer: any = (props: Props) => { appInsights.trackEvent({ name: "Save Settings" }); } const provider = facility.orderingProvider; - const saveFacilityMutation = props.facilityId + const saveFacilityMutation = facilityId ? updateFacilityMutation : addFacilityMutation; const savedFacility = await saveFacilityMutation({ variables: { - facilityId: props.facilityId, + facilityId, testingFacilityName: facility.name, cliaNumber: facility.cliaNumber, street: facility.street, @@ -249,7 +249,7 @@ const FacilityFormContainer: any = (props: Props) => { const getFacilityData = (): Facility => { const facility = data.organization.testingFacility.find( - (f) => f.id === props.facilityId + (f) => f.id === facilityId ); if (facility) { return facility; diff --git a/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx b/frontend/src/app/Settings/Facility/ManageFacilitiesContainer.test.tsx index c434504c37..38b68006db 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, Route, Routes } from "react-router-dom"; import configureStore from "redux-mock-store"; import { GetManagedFacilitiesDocument } from "../../../generated/graphql"; @@ -130,13 +130,18 @@ const mock = [ describe("ManageFacilitiesContainer", () => { beforeEach(() => { render( - - - - - - - + + + + + } + /> + + + + ); }); diff --git a/frontend/src/app/Settings/ManageSelfRegistrationLinksContainer.tsx b/frontend/src/app/Settings/ManageSelfRegistrationLinksContainer.tsx index dbbda937fa..0ab7b02e00 100644 --- a/frontend/src/app/Settings/ManageSelfRegistrationLinksContainer.tsx +++ b/frontend/src/app/Settings/ManageSelfRegistrationLinksContainer.tsx @@ -1,5 +1,7 @@ import { useQuery, gql } from "@apollo/client"; +import { getUrl } from "../utils/url"; + import { ManageSelfRegistrationLinks } from "./ManageSelfRegistrationLinks"; type Data = { @@ -55,7 +57,7 @@ export const ManageSelfRegistrationLinksContainer = () => { return ( > = ({ match }) => { +const Settings = () => { useDocumentTitle("Settings"); + return (
- - - ) => ( - - )} - /> + + } /> ) => ( - - )} + path="facility/:facilityId" + element={} /> + } /> } /> } /> - - + } /> +
); diff --git a/frontend/src/app/Settings/SettingsNav.tsx b/frontend/src/app/Settings/SettingsNav.tsx index 9a6ca51417..f72f98b2de 100644 --- a/frontend/src/app/Settings/SettingsNav.tsx +++ b/frontend/src/app/Settings/SettingsNav.tsx @@ -3,28 +3,37 @@ import React from "react"; import { LinkWithQuery } from "../commonComponents/LinkWithQuery"; const SettingsNav = () => { + const classNameByActive = ({ isActive }: { isActive: boolean }) => + isActive ? "active" : ""; + return (