Skip to content
Snippets Groups Projects
Verified Commit 4f05504d authored by Andreas Ellewsen's avatar Andreas Ellewsen Committed by Andreas Ellewsen
Browse files

Upgrade react-router-dom to v6

Refactors things to new syntax while not changing anything
parent 72a0cb03
No related branches found
No related tags found
1 merge request!308Upgrade react-router-dom to v6
Showing
with 212 additions and 461 deletions
This diff is collapsed.
......@@ -21,7 +21,6 @@
"@types/react": "^17.0.33",
"@types/react-dom": "^17.0.10",
"@types/react-helmet": "^6.1.4",
"@types/react-router-dom": "^5.3.2",
"date-fns": "^2.24.0",
"fetch-intercept": "^2.4.0",
"http-proxy-middleware": "^2.0.1",
......@@ -37,7 +36,7 @@
"react-helmet": "^6.1.0",
"react-hook-form": "^7.17.5",
"react-i18next": "^11.11.4",
"react-router-dom": "^5.3.0",
"react-router-dom": "^6.3.0",
"react-scripts": "4.0.3",
"typeface-roboto": "^1.1.13",
"typescript": "^4.4.4",
......
import React from 'react'
import { Route, RouteProps, useLocation, useHistory } from 'react-router-dom'
import { setCookie } from 'utils'
import { useUserContext } from 'contexts'
import { useLocation, Navigate } from 'react-router-dom'
interface IProtectedRoute extends RouteProps {
children: React.ReactNode
}
function ProtectedRoute({ children, ...rest }: IProtectedRoute) {
// Simple protected route component
const isAuthenticated = () => {
const { user } = useUserContext()
if ( !user.auth ) {
if (!user.auth) {
const location = useLocation()
const history = useHistory()
setCookie('redirect', location.pathname)
history.push('/')
return false
}
return (
<Route {...rest}>
{children}
</Route>
)
return true
}
export default ProtectedRoute
const RequireAuth = ({ children }: { children: JSX.Element }) =>
isAuthenticated() ? children : <Navigate to="/" />
export default RequireAuth
......@@ -2,7 +2,7 @@ import { FetchedGuest, FetchedRole, Guest } from 'interfaces'
import { useEffect, useState } from 'react'
import { parseRole, parseIdentity } from 'utils'
const useGuest = (pid: string) => {
const useGuest = (pid: string ) => {
const [guestInfo, setGuest] = useState<Guest>({
pid: '',
first: '',
......
......@@ -2,7 +2,7 @@ import PersonIcon from '@mui/icons-material/Person'
import { Box, IconButton } from '@mui/material'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
......@@ -15,10 +15,10 @@ export default function OverviewGuestButton(
) {
const { backArrow } = properties
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const goToGuestOverview = () => {
history.push('/guestregister')
navigate('/guestregister')
}
return (
......@@ -33,7 +33,7 @@ export default function OverviewGuestButton(
{backArrow !== undefined ? (
<ArrowBackIcon
onClick={() => {
history.push(backArrow)
navigate(backArrow)
}}
/>
) : (
......
......@@ -3,7 +3,7 @@ import { Box, IconButton, styled, Theme } from '@mui/material'
import PersonAddIcon from '@mui/icons-material/PersonAdd'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
interface SponsorGuestButtonsProps {
yourGuestsActive?: boolean
......@@ -20,14 +20,14 @@ const StyledIconButton = styled(IconButton)({
export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) {
const { yourGuestsActive, registerNewGuestActive } = props
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const goToOverview = () => {
history.push('/sponsor')
navigate('/sponsor')
}
const goToRegister = () => {
history.push('/register')
navigate('/register')
}
return (
......
import React from 'react'
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'
import { render, screen } from 'test-utils'
import AdapterDateFns from '@mui/lab/AdapterDateFns'
import { LocalizationProvider } from '@mui/lab'
import { BrowserRouter } from 'react-router-dom'
import { render, screen } from 'test-utils'
import { FeatureContext } from 'contexts'
import GuestRegister from './index'
......@@ -44,11 +46,13 @@ test('Field showing values correctly', async () => {
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
await screen.findByDisplayValue(testData.person.first_name)
......@@ -88,11 +92,13 @@ test('Gender and birth date suggestions not showing if no national ID given', as
Promise.resolve<any>(JSON.stringify(testData))
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
// Wait a bit so that all the values are showing
......@@ -121,11 +127,13 @@ test('Gender and birth date suggestions not overriding existing values', async (
Promise.resolve<any>(JSON.stringify(testData))
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
// In this a date of birth was already set, and it should not have been overridden by a suggestion
......
import React, { Suspense, useContext, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import {
CountryCode,
getCountries,
......@@ -67,7 +67,7 @@ type InvitationData = {
*/
export default function GuestRegister() {
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const { showGenderFieldForGuest } = useContext(FeatureContext)
const guestRegisterRef = useRef<GuestRegisterCallableMethods>(null)
......@@ -369,7 +369,7 @@ export default function GuestRegister() {
// Go back to the invite page. The invitation ID does not need
// to be specified here, since the user has already been
// logged in and can be identified without the invitation ID
history.push('/invite')
navigate('/invite')
}
return (
......
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { Button, Typography } from '@mui/material'
import { useUserContext } from 'contexts'
const GuestSuccessStep = () => {
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const { getUserInfo } = useUserContext()
// Refetch user info after submission, so that the front page doesn't redirect us
......@@ -36,7 +36,7 @@ const GuestSuccessStep = () => {
marginTop: '2rem',
}}
onClick={() => {
history.push('/')
navigate('/')
}}
>
{t('button.backToFrontPage')}
......
import { useEffect } from 'react'
import { Switch, Route, useHistory } from 'react-router-dom'
import { Routes, Route, useNavigate } from 'react-router-dom'
import { styled } from '@mui/system'
import { CssBaseline } from '@mui/material'
......@@ -22,7 +22,7 @@ import LogoutInviteSession from 'routes/invite/logout'
import Footer from 'routes/components/footer'
import Header from 'routes/components/header'
import NotFound from 'routes/components/notFound'
import ProtectedRoute from 'components/protectedRoute'
import RequireAuth from 'components/requireAuth'
const AppWrapper = styled('div')({
display: 'flex',
......@@ -34,14 +34,14 @@ const AppWrapper = styled('div')({
export default function App() {
const { user, clearUserInfo } = useUserContext()
const history = useHistory()
const navigate = useNavigate()
useEffect(() => {
if (user.auth) {
const redirect = getCookie('redirect')
if (redirect) {
deleteCookie('redirect')
history.push(redirect)
navigate(redirect)
}
}
}, [user.fetched])
......@@ -67,23 +67,31 @@ export default function App() {
<CssBaseline />
<AppWrapper>
<Header />
<Switch>
<Route exact path="/">
<FrontPage />
</Route>
<ProtectedRoute path="/sponsor">
<Sponsor />
</ProtectedRoute>
<ProtectedRoute path="/register">
<Register />
</ProtectedRoute>
<Route path="/invite/logout" component={LogoutInviteSession} />
<Route path="/invite/" component={Invite} />
<Route path="/guestregister" component={GuestRegister} />
<Route>
<NotFound />
</Route>
</Switch>
<Routes>
<Route path="/" element={<FrontPage />} />
<Route
path="/sponsor/*"
element={
<RequireAuth>
<Sponsor />
</RequireAuth>
}
/>
<Route
path="/register/*"
element={
<RequireAuth>
<Register />
</RequireAuth>
}
/>
<Route path="/invite/logout" element={<LogoutInviteSession />} />
<Route path="/invite/" element={<Invite />} />
<Route path="/guestregister" element={<GuestRegister />} />
<Route element={<NotFound />} />
</Routes>
<Footer />
</AppWrapper>
</>
......
import { useEffect, useState } from 'react'
import { Redirect } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { useCookies } from 'react-cookie'
import { Box, CircularProgress } from '@mui/material'
......@@ -20,7 +20,7 @@ export default function LogoutInviteSession() {
}, [])
if (loggedOut) {
return <Redirect to="/" />
return <Navigate to="/" />
}
return (
......
......@@ -13,7 +13,7 @@ import { useUserContext } from 'contexts'
import { FetchedRole, Role, Consent, FetchedConsent } from 'interfaces'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Redirect } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { parseRole, parseConsent } from 'utils'
const GuestPage = () => {
......@@ -123,11 +123,11 @@ function LandingPage() {
const { user } = useUserContext()
if (user.sponsor_id) {
return <Redirect to="/sponsor" />
return <Navigate to="/sponsor" />
}
if (user.person_id) {
if (!user.registration_completed_date) {
return <Redirect to="/guestregister" />
return <Navigate to="/guestregister" />
}
return <GuestPage />
}
......
import { Link, useParams, useHistory } from 'react-router-dom'
import { Link, useParams, useNavigate } from 'react-router-dom'
import Page from 'components/page'
import RoleLine from 'components/roleLine'
......@@ -95,7 +95,7 @@ export default function GuestInfo({
}: GuestInfoProps) {
const { pid } = useParams<GuestInfoParams>()
const [t] = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const [confirmCancelDialogOpen, setConfirmCancelDialogOpen] = useState(false)
const [emailUpdateError, setEmailUpdateError] =
useState<ServerErrorReportData>()
......@@ -196,7 +196,7 @@ export default function GuestInfo({
if (result !== null) {
// The invite for the guest has been cancelled, send the user back to the sponsor front page
reloadGuests()
history.push('/sponsor')
navigate('/sponsor')
}
})
.catch((error) => {
......@@ -365,9 +365,12 @@ export default function GuestInfo({
<TableHeadCell align="left">{t('common:host')}</TableHeadCell>
<TableHeadCell align="left" />
</TableRow>
{guest.roles.map((role) => (
<RoleLine key={role.id} pid={pid} role={role} />
))}
{guest.roles.map((role) => {
if (pid === undefined) {
return <></>
}
return <RoleLine key={role.id} pid={pid} role={role} />
})}
</TableBody>
</Table>
<Button
......
......@@ -3,10 +3,9 @@ import { render, screen } from 'test-utils'
import AdapterDateFns from '@mui/lab/AdapterDateFns'
import { LocalizationProvider } from '@mui/lab'
// eslint-disable-next-line import/no-extraneous-dependencies
import { createMemoryHistory } from 'history'
import { Guest } from 'interfaces'
import parse from 'date-fns/parse'
import { Router } from 'react-router-dom'
import { BrowserRouter } from 'react-router-dom'
import GuestRoleInfo from './index'
import { waitFor } from '../../../../test-utils'
......@@ -52,14 +51,12 @@ const guest: Guest = {
}
test('Button state correct on load', async () => {
const history = createMemoryHistory()
render(
<Router history={history}>
<BrowserRouter>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRoleInfo guest={guest} reloadGuest={() => {}} />
</LocalizationProvider>
</Router>
</BrowserRouter>
)
await waitFor(
......
......@@ -11,7 +11,7 @@ import TableCellMui from '@mui/material/TableCell'
import Page from 'components/page'
import { Guest, Role } from 'interfaces'
import { useTranslation } from 'react-i18next'
import { useHistory, useParams } from 'react-router-dom'
import { useNavigate, useParams } from 'react-router-dom'
import SponsorInfoButtons from 'routes/components/sponsorInfoButtons'
import { DatePicker } from '@mui/lab'
import { Controller, SubmitHandler, useForm } from 'react-hook-form'
......@@ -103,7 +103,7 @@ export default function GuestRoleInfo({
const { pid, id } = useParams<GuestRoleInfoParams>()
const [t] = useTranslation('common')
const { displayContactAtUnit, displayComment } = useFeatureContext()
const history = useHistory()
const navigate = useNavigate()
const [role, setRole] = useState<Role>({
id: '',
name_nb: '',
......@@ -122,15 +122,15 @@ export default function GuestRoleInfo({
const todayPlusMaxDays = addDays(role.max_days)(today)
// Make a function for use with onClick of the end role button
const endPeriod = () => {
const endPeriod = (role_id: string) => () => {
// Set the role to have ended yesterday so that the change is immediate.
// If the role was set to end today, the change will not be active
// until the next date since the end date is inclusive
const newEndDate = addDays(-1)(today)
role.end_date = newEndDate
endPeriodPost(id, { end_date: newEndDate })
endPeriodPost(role_id, { end_date: newEndDate })
// Go back to guest overview page
history.push(`/sponsor/guest/${guest.pid}`)
navigate(`/sponsor/guest/${guest.pid}`)
}
const {
......@@ -314,14 +314,18 @@ export default function GuestRoleInfo({
>
{t('sponsor.endNow')}
</Button>
<ConfirmDialog
title={t('endRoleDialog.title')}
open={showEndRoleConfirmationDialog}
setOpen={setShowEndRoleConfirmationDialog}
onConfirm={endPeriod}
>
{t('endRoleDialog.text')}
</ConfirmDialog>
{typeof id === 'string' ? (
<ConfirmDialog
title={t('endRoleDialog.title')}
open={showEndRoleConfirmationDialog}
setOpen={setShowEndRoleConfirmationDialog}
onConfirm={endPeriod(id)}
>
{t('endRoleDialog.text')}
</ConfirmDialog>
) : (
<></>
)}
</form>
</Page>
)
......
import useGuest from 'hooks/useGuest'
import { Route, useParams } from 'react-router-dom'
import { Route, Routes, useParams } from 'react-router-dom'
import { submitJsonOpts } from 'utils'
import GuestInfo from './guestInfo'
import GuestRoleInfo from './guestRoleInfo'
......@@ -15,6 +15,9 @@ type GuestRoutesProps = {
function GuestRoutes({ reloadGuests }: GuestRoutesProps) {
const { pid } = useParams<GuestInfoParams>()
if (typeof pid === 'undefined') {
return <></>
}
const { guestInfo, reloadGuestInfo } = useGuest(pid)
const updateEmail = (
......@@ -59,23 +62,32 @@ function GuestRoutes({ reloadGuests }: GuestRoutesProps) {
}
return (
<>
<Route path="/sponsor/guest/:pid/roles/:id">
<GuestRoleInfo guest={guestInfo} reloadGuest={reloadGuestInfo} />
</Route>
<Route exact path="/sponsor/guest/:pid/newrole">
<NewGuestRole guest={guestInfo} reloadGuestInfo={reloadGuestInfo} />
</Route>
<Route exact path="/sponsor/guest/:pid">
<GuestInfo
guest={guestInfo}
updateEmail={updateEmail}
resend={resend}
reloadGuests={reloadGuests}
reloadGuest={reloadGuestInfo}
/>
</Route>
</>
<Routes>
<Route
path="/roles/:id"
element={
<GuestRoleInfo guest={guestInfo} reloadGuest={reloadGuestInfo} />
}
/>
<Route
path="/newrole"
element={
<NewGuestRole guest={guestInfo} reloadGuestInfo={reloadGuestInfo} />
}
/>
<Route
path=""
element={
<GuestInfo
guest={guestInfo}
updateEmail={updateEmail}
resend={resend}
reloadGuests={reloadGuests}
reloadGuest={reloadGuestInfo}
/>
}
/>
</Routes>
)
}
......
import { useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { Link, useHistory, useParams } from 'react-router-dom'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { format } from 'date-fns'
import { addDays } from 'date-fns/fp'
import { DatePicker } from '@mui/lab'
......@@ -110,11 +110,16 @@ function NewGuestRole({ guest, reloadGuestInfo }: NewGuestRoleProps) {
const { displayContactAtUnit, displayComment } = useFeatureContext()
const { pid } = useParams<GuestInfoParams>()
const history = useHistory()
// Hack to make typecheck happy. The only way for pid to be undefined
// is if the Route is set up wrong, which should brake everything any way
if(typeof(pid) === "undefined"){
return <></>
}
const navigate = useNavigate()
const onSubmit = handleSubmit(async () => {
await postRole(getValues(), pid)
reloadGuestInfo()
history.push(`/sponsor/guest/${pid}`)
navigate(`/sponsor/guest/${pid}`)
})
const ous = useOus()
......
import { Route } from 'react-router-dom'
import { Route, Routes } from 'react-router-dom'
import FrontPage from 'routes/sponsor/frontpage'
import useGuests from 'hooks/useGuests'
......@@ -8,14 +8,13 @@ function Sponsor() {
const { guests, reloadGuests } = useGuests()
return (
<>
<Route path="/sponsor/guest/:pid">
<GuestRoutes reloadGuests={reloadGuests} />
</Route>
<Route exact path="/sponsor">
<FrontPage guests={guests} />
</Route>
</>
<Routes>
<Route
path="guest/:pid/*"
element={<GuestRoutes reloadGuests={reloadGuests} />}
/>
<Route path="" element={<FrontPage guests={guests} />} />
</Routes>
)
}
......
import React from 'react'
import { render, waitFor, screen } from 'test-utils'
import userEvent from '@testing-library/user-event'
import AdapterDateFns from '@mui/lab/AdapterDateFns'
import { LocalizationProvider } from '@mui/lab'
import { BrowserRouter } from 'react-router-dom'
import { render, waitFor, screen } from 'test-utils'
import StepRegistration from './stepRegistration'
test('Validation message showing if last name is missing', async () => {
render(
<LocalizationProvider dateAdapter={AdapterDateFns}>
<StepRegistration />
</LocalizationProvider>
<BrowserRouter>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<StepRegistration />
</LocalizationProvider>
</BrowserRouter>
)
// Try to go to the next step and check that the validation message is showing
......
import { Route } from 'react-router-dom'
import { Route, Routes } from 'react-router-dom'
import FrontPage from './frontPage'
import StepRegistration from './stepRegistration'
function Register() {
return (
<>
<Route path="/register/new">
<StepRegistration />
</Route>
<Route exact path="/register">
<FrontPage />
</Route>
</>
<Routes>
<Route path="/new" element={<StepRegistration />} />
<Route path="" element={<FrontPage />} />
</Routes>
)
}
export default Register
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment