From 5688b0fc15831119c3dbce73027a45acfb7cec93 Mon Sep 17 00:00:00 2001 From: Lisa Chan Date: Wed, 17 Jun 2026 18:29:53 -0400 Subject: [PATCH 1/2] refactor(settings): Use react-router instead of reach/router --- packages/fxa-admin-panel/package.json | 2 +- packages/fxa-admin-panel/src/App.tsx | 2 +- .../src/components/Nav/index.tsx | 2 +- .../PageRelyingParties/index.test.tsx | 2 +- .../components/PageRelyingParties/index.tsx | 2 +- .../components/PageWafTokens/index.test.tsx | 2 +- .../src/components/PageWafTokens/index.tsx | 2 +- packages/fxa-admin-panel/src/setupTests.js | 6 + packages/fxa-react/lib/storybooks.tsx | 28 +- packages/fxa-react/package.json | 3 + packages/fxa-settings/package.json | 4 +- .../AlternativeAuthOptions/mocks.tsx | 6 +- .../src/components/App/index.test.tsx | 268 +++++----- .../fxa-settings/src/components/App/index.tsx | 493 +++++++----------- .../src/components/Banner/index.tsx | 2 +- .../index.test.tsx | 23 +- .../ErrorBoundaries/index.stories.tsx | 4 +- .../components/FormPassword/index.stories.tsx | 10 +- .../components/FormPassword/index.test.tsx | 4 +- .../src/components/FormPassword/index.tsx | 2 +- .../components/FormPhoneNumber/index.test.tsx | 21 +- .../LegalWithMarkdown/index.test.tsx | 9 +- .../components/LegalWithMarkdown/index.tsx | 3 +- .../components/LinkExpired/index.stories.tsx | 6 +- .../LinkRememberPassword/index.stories.tsx | 6 +- .../LinkRememberPassword/index.test.tsx | 44 +- .../components/LinkRememberPassword/index.tsx | 2 +- .../PromoQrMobile/index.stories.tsx | 26 +- .../components/PromoQrMobile/index.test.tsx | 14 +- .../src/components/PromoQrMobile/index.tsx | 2 +- .../PromotionBanner/index.stories.tsx | 6 +- .../components/PromotionBanner/index.test.tsx | 17 +- .../src/components/PromotionBanner/index.tsx | 2 +- .../src/components/Ready/index.tsx | 6 +- .../ResetPasswordWarning/index.stories.tsx | 14 +- .../ResetPasswordWarning/index.test.tsx | 31 +- .../components/ResetPasswordWarning/index.tsx | 2 +- .../index.stories.tsx | 6 +- .../ConnectedServices/index.stories.tsx | 6 +- .../Settings/FlowContainer/index.stories.tsx | 6 +- .../Settings/FlowContainer/index.tsx | 4 +- .../FlowRecoveryKeyConfirmPwd/index.tsx | 2 +- .../FlowRecoveryKeyDownload/index.test.tsx | 2 +- .../FlowRecoveryKeyHint/index.stories.tsx | 6 +- .../Settings/FlowRecoveryKeyInfo/index.tsx | 2 +- .../Settings/HeaderLockup/index.stories.tsx | 14 +- .../Settings/HeaderLockup/index.test.tsx | 3 - .../Settings/HeaderLockup/index.tsx | 2 +- .../Settings/LinkedAccounts/LinkedAccount.tsx | 2 +- .../Settings/LinkedAccounts/index.stories.tsx | 14 +- .../Settings/LinkedAccounts/index.test.tsx | 4 +- .../Settings/MfaGuard/index.test.tsx | 4 +- .../components/Settings/MfaGuard/index.tsx | 2 +- .../Settings/Modal/index.stories.tsx | 6 +- .../src/components/Settings/Modal/index.tsx | 2 +- .../ModalVerifySession/index.stories.tsx | 6 +- .../components/Settings/Nav/index.stories.tsx | 6 +- .../Settings/Page2faChange/index.tsx | 3 +- .../Settings/Page2faChange/mocks.tsx | 26 +- .../index.stories.tsx | 10 +- .../Page2faReplaceBackupCodes/index.test.tsx | 4 +- .../Page2faReplaceBackupCodes/index.tsx | 9 +- .../Settings/Page2faSetup/index.tsx | 3 +- .../Settings/Page2faSetup/mocks.tsx | 26 +- .../Settings/PageAvatar/index.stories.tsx | 6 +- .../components/Settings/PageAvatar/index.tsx | 4 +- .../PageChangePassword/index.stories.tsx | 6 +- .../PageChangePassword/index.test.tsx | 4 +- .../Settings/PageChangePassword/index.tsx | 6 +- .../PageCreatePassword/index.stories.tsx | 6 +- .../PageCreatePassword/index.test.tsx | 8 +- .../Settings/PageCreatePassword/index.tsx | 8 +- .../PageDeleteAccount/index.stories.tsx | 6 +- .../Settings/PageDeleteAccount/index.tsx | 4 +- .../PageDisplayName/index.stories.tsx | 6 +- .../Settings/PageDisplayName/index.test.tsx | 14 +- .../Settings/PageDisplayName/index.tsx | 4 +- .../Settings/PageMfaGuardTest/index.tsx | 8 +- .../Settings/PagePasskeyAdd/index.stories.tsx | 27 +- .../Settings/PagePasskeyAdd/index.test.tsx | 6 +- .../Settings/PagePasskeyAdd/index.tsx | 4 +- .../Settings/PageRecentActivity/index.tsx | 4 +- .../PageRecoveryKeyCreate/index.stories.tsx | 6 +- .../PageRecoveryKeyCreate/index.test.tsx | 4 +- .../Settings/PageRecoveryKeyCreate/index.tsx | 8 +- .../PageRecoveryPhoneRemove/index.stories.tsx | 6 +- .../PageRecoveryPhoneRemove/index.test.tsx | 4 +- .../PageRecoveryPhoneRemove/index.tsx | 8 +- .../PageRecoveryPhoneSetup/index.stories.tsx | 32 +- .../PageRecoveryPhoneSetup/index.test.tsx | 4 +- .../Settings/PageRecoveryPhoneSetup/index.tsx | 8 +- .../Settings/PageRecoveryPhoneSetup/mocks.tsx | 13 +- .../PageSecondaryEmailAdd/index.stories.tsx | 6 +- .../PageSecondaryEmailAdd/index.test.tsx | 12 +- .../Settings/PageSecondaryEmailAdd/index.tsx | 6 +- .../index.stories.tsx | 25 +- .../PageSecondaryEmailVerify/index.test.tsx | 70 +-- .../PageSecondaryEmailVerify/index.tsx | 12 +- .../Settings/PageSettings/index.stories.tsx | 6 +- .../Settings/PageSettings/index.tsx | 4 +- .../Settings/Profile/index.stories.tsx | 6 +- .../Settings/ProgressBar/index.stories.tsx | 6 +- .../components/Settings/ScrollToTop/index.tsx | 36 +- .../Settings/Security/index.stories.tsx | 6 +- .../components/Settings/Security/index.tsx | 2 +- .../Settings/SettingsLayout/index.test.tsx | 4 +- .../components/Settings/SignoutSync/index.tsx | 3 +- .../components/Settings/SubRow/index.test.tsx | 6 +- .../Settings/UnitRow/index.stories.tsx | 6 +- .../src/components/Settings/UnitRow/index.tsx | 4 +- .../Settings/UnitRowPasskey/index.stories.tsx | 6 +- .../Settings/UnitRowPasskey/index.test.tsx | 6 +- .../UnitRowRecoveryKey/index.stories.tsx | 6 +- .../UnitRowSecondaryEmail/index.stories.tsx | 6 +- .../UnitRowSecondaryEmail/index.test.tsx | 4 +- .../UnitRowTwoStepAuth/index.stories.tsx | 6 +- .../src/components/Settings/index.test.tsx | 66 ++- .../src/components/Settings/index.tsx | 91 ++-- .../src/components/Settings/mocks.tsx | 16 +- .../TermsPrivacyAgreement/index.test.tsx | 41 +- .../TermsPrivacyAgreement/index.tsx | 2 +- .../src/components/ThirdPartyAuth/mocks.tsx | 6 +- packages/fxa-settings/src/index.tsx | 28 +- .../lib/hooks/useNavigateWithQuery/index.tsx | 35 +- .../useNavigateWithoutRerender/index.tsx | 4 +- .../src/lib/hooks/useWebRedirect/index.tsx | 4 +- .../lib/integrations/integration-factory.ts | 4 +- .../model-data/data-stores/storage-data.ts | 4 +- .../lib/model-data/data-stores/url-data.ts | 4 +- .../model-data/data-stores/url-hash-data.ts | 4 +- .../data-stores/url-query-data.test.ts | 19 +- .../model-data/data-stores/url-query-data.ts | 13 +- .../src/lib/passkeys/signin-flow.test.tsx | 73 +-- .../src/lib/passkeys/signin-flow.ts | 4 + .../fxa-settings/src/lib/storybook-utils.ts | 4 +- packages/fxa-settings/src/lib/utilities.ts | 48 +- packages/fxa-settings/src/lib/window.test.ts | 8 +- packages/fxa-settings/src/lib/window.ts | 28 +- packages/fxa-settings/src/models/mocks.tsx | 102 ++-- .../pages/Authorization/container.test.tsx | 31 +- .../src/pages/Authorization/container.tsx | 7 +- .../fxa-settings/src/pages/Clear/index.tsx | 3 +- .../ConnectAnotherDevice/index.stories.tsx | 6 +- .../src/pages/ConnectAnotherDevice/index.tsx | 4 +- .../src/pages/CookiesDisabled/index.tsx | 4 +- .../src/pages/Index/container.test.tsx | 66 +-- .../src/pages/Index/container.tsx | 4 +- .../fxa-settings/src/pages/Index/index.tsx | 2 +- .../fxa-settings/src/pages/Index/mocks.tsx | 6 +- .../InlineRecoveryKeySetup/container.test.tsx | 18 +- .../InlineRecoveryKeySetup/container.tsx | 4 +- .../pages/InlineRecoveryKeySetup/index.tsx | 3 +- .../container.test.tsx | 10 +- .../InlineRecoverySetupFlow/container.tsx | 4 +- .../InlineRecoverySetupFlow/index.stories.tsx | 6 +- .../pages/InlineRecoverySetupFlow/index.tsx | 4 +- .../pages/InlineTotpSetup/container.test.tsx | 10 +- .../src/pages/InlineTotpSetup/container.tsx | 4 +- .../src/pages/Legal/Privacy/index.test.tsx | 5 +- .../src/pages/Legal/Privacy/index.tsx | 4 +- .../src/pages/Legal/Terms/index.test.tsx | 5 +- .../src/pages/Legal/Terms/index.tsx | 4 +- .../src/pages/Legal/index.test.tsx | 3 +- .../fxa-settings/src/pages/Legal/index.tsx | 4 +- .../pages/Pair/AuthAllow/index.stories.tsx | 6 +- .../src/pages/Pair/AuthAllow/index.test.tsx | 17 +- .../src/pages/Pair/AuthAllow/index.tsx | 4 +- .../pages/Pair/AuthComplete/index.test.tsx | 55 +- .../src/pages/Pair/AuthComplete/index.tsx | 4 +- .../src/pages/Pair/AuthTotp/index.stories.tsx | 6 +- .../src/pages/Pair/AuthTotp/index.tsx | 4 +- .../Pair/AuthWaitForSupp/index.stories.tsx | 6 +- .../src/pages/Pair/AuthWaitForSupp/index.tsx | 4 +- .../src/pages/Pair/Failure/index.tsx | 3 +- .../src/pages/Pair/Index/index.stories.tsx | 49 +- .../src/pages/Pair/Index/index.test.tsx | 4 +- .../src/pages/Pair/Index/index.tsx | 4 +- .../src/pages/Pair/Success/index.tsx | 3 +- .../src/pages/Pair/Supp/index.stories.tsx | 6 +- .../src/pages/Pair/Supp/index.tsx | 4 +- .../pages/Pair/SuppAllow/index.stories.tsx | 6 +- .../src/pages/Pair/SuppAllow/index.tsx | 4 +- .../Pair/SuppWaitForAuth/index.stories.tsx | 6 +- .../src/pages/Pair/SuppWaitForAuth/index.tsx | 4 +- .../src/pages/Pair/Unsupported/index.tsx | 4 +- .../PostVerify/ServiceWelcome/index.test.tsx | 13 +- .../pages/PostVerify/ServiceWelcome/index.tsx | 4 +- .../pages/PostVerify/ServiceWelcome/mocks.tsx | 21 +- .../PostVerify/SetPassword/container.test.tsx | 10 +- .../PostVerify/SetPassword/container.tsx | 7 +- .../pages/PostVerify/SetPassword/mocks.tsx | 6 +- .../ThirdPartyAuthCallback/index.test.tsx | 11 +- .../ThirdPartyAuthCallback/index.tsx | 7 +- .../AccountRecoveryConfirmKey/container.tsx | 4 +- .../AccountRecoveryConfirmKey/index.tsx | 2 +- .../AccountRecoveryConfirmKey/mocks.tsx | 6 +- .../CompleteResetPassword/container.test.tsx | 10 +- .../CompleteResetPassword/container.tsx | 4 +- .../CompleteResetPassword/index.tsx | 2 +- .../CompleteResetPassword/mocks.tsx | 6 +- .../container.test.tsx | 10 +- .../container.tsx | 4 +- .../ConfirmBackupCodeResetPassword/mocks.tsx | 6 +- .../ConfirmResetPassword/container.tsx | 4 +- .../ConfirmResetPassword/index.test.tsx | 30 +- .../ConfirmResetPassword/index.tsx | 4 +- .../ConfirmResetPassword/mocks.tsx | 6 +- .../container.test.tsx | 10 +- .../ConfirmTotpResetPassword/container.tsx | 4 +- .../ConfirmTotpResetPassword/mocks.tsx | 6 +- .../ResetPassword/ResetPassword/container.tsx | 3 +- .../ResetPassword/ResetPassword/index.tsx | 3 +- .../ResetPassword/ResetPassword/mocks.tsx | 6 +- .../ResetPasswordConfirmed/container.tsx | 4 +- .../ResetPasswordConfirmed/index.tsx | 3 +- .../container.test.tsx | 36 +- .../ResetPasswordRecoveryChoice/container.tsx | 3 +- .../index.stories.tsx | 22 +- .../index.test.tsx | 37 +- .../container.test.tsx | 10 +- .../ResetPasswordRecoveryPhone/container.tsx | 4 +- .../ResetPasswordRecoveryPhone/index.test.tsx | 99 ++-- .../ResetPasswordRecoveryPhone/index.tsx | 7 +- .../ResetPasswordRecoveryPhone/mocks.tsx | 6 +- .../container.tsx | 4 +- .../index.test.tsx | 7 +- .../Signin/CompleteSignin/container.test.tsx | 10 +- .../pages/Signin/CompleteSignin/container.tsx | 4 +- .../Signin/CompleteSignin/index.test.tsx | 6 +- .../src/pages/Signin/CompleteSignin/index.tsx | 3 +- .../Signin/ReportSignin/container.test.tsx | 10 +- .../pages/Signin/ReportSignin/container.tsx | 4 +- .../Signin/ReportSignin/index.stories.tsx | 10 +- .../src/pages/Signin/ReportSignin/index.tsx | 4 +- .../Signin/SigninBounced/index.stories.tsx | 10 +- .../pages/Signin/SigninBounced/index.test.tsx | 4 +- .../src/pages/Signin/SigninBounced/index.tsx | 3 +- .../Signin/SigninConfirmed/index.stories.tsx | 18 +- .../pages/Signin/SigninConfirmed/index.tsx | 3 +- .../SigninPasskeyFallback/container.test.tsx | 10 +- .../SigninPasskeyFallback/container.tsx | 7 +- .../SigninPasskeyFallback/index.stories.tsx | 10 +- .../Signin/SigninPasskeyFallback/index.tsx | 4 +- .../SigninPasswordlessCode/container.test.tsx | 10 +- .../SigninPasswordlessCode/container.tsx | 4 +- .../SigninPasswordlessCode/index.test.tsx | 8 +- .../Signin/SigninPasswordlessCode/index.tsx | 5 +- .../Signin/SigninPasswordlessCode/mocks.tsx | 6 +- .../SigninRecoveryChoice/container.test.tsx | 42 +- .../Signin/SigninRecoveryChoice/container.tsx | 4 +- .../SigninRecoveryChoice/index.stories.tsx | 30 +- .../SigninRecoveryChoice/index.test.tsx | 37 +- .../SigninRecoveryCode/container.test.tsx | 36 +- .../Signin/SigninRecoveryCode/container.tsx | 4 +- .../SigninRecoveryCode/index.stories.tsx | 14 +- .../Signin/SigninRecoveryCode/index.test.tsx | 58 +-- .../pages/Signin/SigninRecoveryCode/index.tsx | 7 +- .../SigninRecoveryPhone/container.test.tsx | 31 +- .../Signin/SigninRecoveryPhone/container.tsx | 6 +- .../Signin/SigninRecoveryPhone/index.test.tsx | 27 +- .../Signin/SigninRecoveryPhone/index.tsx | 4 +- .../Signin/SigninRecoveryPhone/mocks.tsx | 6 +- .../Signin/SigninReported/index.stories.tsx | 6 +- .../src/pages/Signin/SigninReported/index.tsx | 4 +- .../Signin/SigninTokenCode/container.test.tsx | 10 +- .../Signin/SigninTokenCode/container.tsx | 4 +- .../Signin/SigninTokenCode/index.test.tsx | 20 +- .../pages/Signin/SigninTokenCode/index.tsx | 7 +- .../pages/Signin/SigninTokenCode/mocks.tsx | 25 +- .../Signin/SigninTotpCode/container.test.tsx | 32 +- .../pages/Signin/SigninTotpCode/container.tsx | 4 +- .../Signin/SigninTotpCode/index.stories.tsx | 6 +- .../Signin/SigninTotpCode/index.test.tsx | 216 ++++---- .../src/pages/Signin/SigninTotpCode/index.tsx | 6 +- .../src/pages/Signin/SigninTotpCode/mocks.tsx | 23 +- .../Signin/SigninUnblock/container.test.tsx | 31 +- .../pages/Signin/SigninUnblock/container.tsx | 4 +- .../Signin/SigninUnblock/index.stories.tsx | 22 +- .../pages/Signin/SigninUnblock/index.test.tsx | 19 +- .../src/pages/Signin/SigninUnblock/index.tsx | 6 +- .../index.stories.tsx | 6 +- .../index.test.tsx | 6 +- .../SigninAlternativeAuthOptions/index.tsx | 4 +- .../components/SigninCached/index.test.tsx | 6 +- .../Signin/components/SigninCached/index.tsx | 7 +- .../components/SigninDecider/index.test.tsx | 4 +- .../Signin/components/SigninDecider/index.tsx | 2 +- .../src/pages/Signin/container.test.tsx | 10 +- .../src/pages/Signin/container.tsx | 4 +- .../src/pages/Signin/index.test.tsx | 9 +- .../fxa-settings/src/pages/Signin/index.tsx | 7 +- .../src/pages/Signin/interfaces.ts | 2 + .../fxa-settings/src/pages/Signin/mocks.tsx | 6 +- .../src/pages/Signin/utils.test.ts | 60 +-- .../fxa-settings/src/pages/Signin/utils.ts | 17 +- .../ConfirmSignupCode/container.test.tsx | 47 +- .../Signup/ConfirmSignupCode/container.tsx | 4 +- .../Signup/ConfirmSignupCode/index.test.tsx | 4 +- .../pages/Signup/ConfirmSignupCode/index.tsx | 4 +- .../Signup/ConfirmSignupCode/interfaces.ts | 4 +- .../pages/Signup/ConfirmSignupCode/mocks.tsx | 6 +- .../Signup/PrimaryEmailVerified/index.tsx | 3 +- .../Signup/SignupConfirmed/index.stories.tsx | 18 +- .../pages/Signup/SignupConfirmed/index.tsx | 3 +- .../Signup/SignupConfirmedSync/index.tsx | 4 +- .../Signup/SignupConfirmedSync/mocks.tsx | 27 +- .../src/pages/Signup/container.test.tsx | 16 +- .../src/pages/Signup/container.tsx | 4 +- .../src/pages/Signup/index.stories.tsx | 6 +- .../src/pages/Signup/index.test.tsx | 4 +- .../fxa-settings/src/pages/Signup/mocks.tsx | 6 +- .../src/pages/WebChannelExample/index.tsx | 4 +- packages/fxa-settings/src/setupTests.tsx | 14 + yarn.lock | 133 +---- 314 files changed, 2213 insertions(+), 2371 deletions(-) diff --git a/packages/fxa-admin-panel/package.json b/packages/fxa-admin-panel/package.json index 8c96f8d4558..9abb1626bb3 100644 --- a/packages/fxa-admin-panel/package.json +++ b/packages/fxa-admin-panel/package.json @@ -47,7 +47,7 @@ "helmet": "^8.0.0", "mozlog": "^3.0.2", "on-headers": "^1.1.0", - "react-router-dom": "^6.26.0", + "react-router": "7.18.0", "react-scripts": "^5.0.1", "serve-static": "^1.16.0" }, diff --git a/packages/fxa-admin-panel/src/App.tsx b/packages/fxa-admin-panel/src/App.tsx index 5ae408d6136..797ac5d5b0e 100644 --- a/packages/fxa-admin-panel/src/App.tsx +++ b/packages/fxa-admin-panel/src/App.tsx @@ -5,7 +5,7 @@ import { useState } from 'react'; import { UserContext } from './hooks/UserContext'; import { GuardContext } from './hooks/GuardContext'; -import { Route, Routes, BrowserRouter, Navigate } from 'react-router-dom'; +import { Route, Routes, BrowserRouter, Navigate } from 'react-router'; import AppLayout from './components/AppLayout'; import AccountSearch from './components/PageAccountSearch'; import { PagePermissions } from './components/PagePermissions'; diff --git a/packages/fxa-admin-panel/src/components/Nav/index.tsx b/packages/fxa-admin-panel/src/components/Nav/index.tsx index d50331eda41..b58cf2d92e7 100644 --- a/packages/fxa-admin-panel/src/components/Nav/index.tsx +++ b/packages/fxa-admin-panel/src/components/Nav/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from 'react'; -import { NavLink } from 'react-router-dom'; +import { NavLink } from 'react-router'; import accountIcon from '../../images/icon-account.svg'; import keyIcon from '../../images/icon-key.svg'; import shieldIcon from '../../images/icon-shield.svg'; diff --git a/packages/fxa-admin-panel/src/components/PageRelyingParties/index.test.tsx b/packages/fxa-admin-panel/src/components/PageRelyingParties/index.test.tsx index f154222b9fa..9f518c18147 100644 --- a/packages/fxa-admin-panel/src/components/PageRelyingParties/index.test.tsx +++ b/packages/fxa-admin-panel/src/components/PageRelyingParties/index.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { act, fireEvent, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; +import { MemoryRouter } from 'react-router'; import PageRelyingParties from '.'; import { MOCK_RP_ALL_FIELDS, MOCK_RP_FALSY_FIELDS } from './mocks'; import { IClientConfig } from '../../../interfaces'; diff --git a/packages/fxa-admin-panel/src/components/PageRelyingParties/index.tsx b/packages/fxa-admin-panel/src/components/PageRelyingParties/index.tsx index 253cd7d60c4..39e2a05af0a 100644 --- a/packages/fxa-admin-panel/src/components/PageRelyingParties/index.tsx +++ b/packages/fxa-admin-panel/src/components/PageRelyingParties/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useState, useEffect, useCallback } from 'react'; -import { NavLink } from 'react-router-dom'; +import { NavLink } from 'react-router'; import LinkExternal from 'fxa-react/components/LinkExternal'; import { RelyingPartyCreatedDto, diff --git a/packages/fxa-admin-panel/src/components/PageWafTokens/index.test.tsx b/packages/fxa-admin-panel/src/components/PageWafTokens/index.test.tsx index 377d49843ab..80e6fcbb162 100644 --- a/packages/fxa-admin-panel/src/components/PageWafTokens/index.test.tsx +++ b/packages/fxa-admin-panel/src/components/PageWafTokens/index.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useLocation } from 'react-router'; import { PageWafTokens } from '.'; import { IClientConfig } from '../../../interfaces'; import { GuardEnv, AdminPanelGroup, AdminPanelGuard } from '@fxa/shared/guards'; diff --git a/packages/fxa-admin-panel/src/components/PageWafTokens/index.tsx b/packages/fxa-admin-panel/src/components/PageWafTokens/index.tsx index f8c09b5e219..797e2156b80 100644 --- a/packages/fxa-admin-panel/src/components/PageWafTokens/index.tsx +++ b/packages/fxa-admin-panel/src/components/PageWafTokens/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useSearchParams } from 'react-router'; import { adminApi } from '../../lib/api'; import { AdminPanelFeature } from '@fxa/shared/guards'; import { Guard } from '../Guard'; diff --git a/packages/fxa-admin-panel/src/setupTests.js b/packages/fxa-admin-panel/src/setupTests.js index 8c81f88ecbd..cd7e5e9680d 100644 --- a/packages/fxa-admin-panel/src/setupTests.js +++ b/packages/fxa-admin-panel/src/setupTests.js @@ -3,3 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import '@testing-library/jest-dom/extend-expect'; +import { TextEncoder, TextDecoder } from 'util'; + +// react-router v7 requires TextEncoder/TextDecoder in its internal modules. +// jsdom does not provide these by default. +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; diff --git a/packages/fxa-react/lib/storybooks.tsx b/packages/fxa-react/lib/storybooks.tsx index d08ea080b9f..4bf28119da8 100644 --- a/packages/fxa-react/lib/storybooks.tsx +++ b/packages/fxa-react/lib/storybooks.tsx @@ -5,11 +5,7 @@ import React from 'react'; import { Decorator } from '@storybook/react'; import AppLocalizationProvider from './AppLocalizationProvider'; -import { - createHistory, - createMemorySource, - LocationProvider, -} from '@reach/router'; +import { MemoryRouter } from 'react-router'; // This decorator makes the localization bundles available in the stories. // If a localized string is available, that will be rendered in the storybook, @@ -21,20 +17,8 @@ export const withLocalization: Decorator = (Story) => ( ); export const withLocation: (location?: string) => Decorator = - (location) => (Story) => { - if (location === undefined) { - return ( - - - - ); - } - const source = createMemorySource(location); - const history = createHistory(source); - - return ( - - - - ); - }; + (location) => (Story) => ( + + + + ); diff --git a/packages/fxa-react/package.json b/packages/fxa-react/package.json index 03484dc1623..6c5d95b88fb 100644 --- a/packages/fxa-react/package.json +++ b/packages/fxa-react/package.json @@ -37,6 +37,9 @@ "test-unit": "JEST_JUNIT_OUTPUT_FILE=../../artifacts/tests/$npm_package_name/fxa-react-jest-unit-results.xml jest --coverage --runInBand --logHeapUsage --env=jest-environment-jsdom -t '^(?!.*?#integration).*' --ci --reporters=default --reporters=jest-junit", "watch-ftl": "yarn l10n-watch" }, + "peerDependencies": { + "react-router": "^7.18.0" + }, "dependencies": { "@fluent/bundle": "^0.18.0", "@fluent/langneg": "^0.7.0", diff --git a/packages/fxa-settings/package.json b/packages/fxa-settings/package.json index f0ea1b42bcc..fe644de72a7 100644 --- a/packages/fxa-settings/package.json +++ b/packages/fxa-settings/package.json @@ -132,7 +132,6 @@ "@emotion/styled": "^11.13.0", "@material-ui/core": "v5.0.0-alpha.24", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@reach/router": "^1.3.4", "@react-pdf/renderer": "3.2.1", "@svgr/webpack": "^8.1.0", "@types/material-ui": "^0.21.8", @@ -178,6 +177,7 @@ "react-hook-form": "^6.15.8", "react-markdown": "^8.0.5", "react-refresh": "^0.16.0", + "react-router": "7.18.0", "react-webcam": "^7.2.0", "rehype-raw": "^6.1.1", "resolve": "^1.22.8", @@ -209,8 +209,6 @@ "@types/jest": "26.0.23", "@types/node": "^22.13.5", "@types/prop-types": "^15", - "@types/reach__router": "^1.3.11", - "@types/react-router": "^5.1.19", "@types/ua-parser-js": "^0.7.36", "@types/uuid": "^10", "@types/webpack": "5.28.5", diff --git a/packages/fxa-settings/src/components/AlternativeAuthOptions/mocks.tsx b/packages/fxa-settings/src/components/AlternativeAuthOptions/mocks.tsx index 6e8733473cd..88e83133e55 100644 --- a/packages/fxa-settings/src/components/AlternativeAuthOptions/mocks.tsx +++ b/packages/fxa-settings/src/components/AlternativeAuthOptions/mocks.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import AlternativeAuthOptions, { AlternativeAuthOptionsProps } from '.'; import Banner from '../Banner'; import { AppContext } from '../../models'; @@ -10,9 +10,9 @@ import { mockAppContext } from '../../models/mocks'; export const Subject = (props: AlternativeAuthOptionsProps) => ( - + - + ); diff --git a/packages/fxa-settings/src/components/App/index.test.tsx b/packages/fxa-settings/src/components/App/index.test.tsx index 4886bc45607..1b31ec4db45 100644 --- a/packages/fxa-settings/src/components/App/index.test.tsx +++ b/packages/fxa-settings/src/components/App/index.test.tsx @@ -7,6 +7,7 @@ import { act, screen, waitFor } from '@testing-library/react'; import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; import * as ReactUtils from 'fxa-react/lib/utils'; import App from '.'; +import { MemoryRouter } from 'react-router'; import * as Metrics from '../../lib/metrics'; import { AppContext, @@ -193,7 +194,7 @@ describe('metrics', () => { afterEach(() => { flowInit.mockReset(); - (useFxAStatus as jest.Mock).mockRestore(); + (useFxAStatus as jest.Mock).mockReset(); }); it('Initializes metrics flow data when present', async () => { @@ -220,11 +221,13 @@ describe('metrics', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -246,7 +249,7 @@ describe('glean', () => { }); afterEach(() => { - (useFxAStatus as jest.Mock).mockRestore(); + (useFxAStatus as jest.Mock).mockReset(); }); it('Initializes glean', async () => { (useInitialMetricsQueryState as jest.Mock).mockReturnValueOnce({ @@ -269,11 +272,13 @@ describe('glean', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -311,11 +316,13 @@ describe('loading spinner states', () => { }); await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -350,11 +357,13 @@ describe('loading spinner states', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -368,7 +377,7 @@ describe('AuthAndAccountSetupRoutes', () => { }); afterEach(() => { - (useFxAStatus as jest.Mock).mockRestore(); + (useFxAStatus as jest.Mock).mockReset(); }); it('calls useFxAStatus with integration', async () => { const mockIntegration = { @@ -459,13 +468,13 @@ describe('SettingsRoutes', () => { value: originalUserAgent, configurable: true, }); - (useIntegration as jest.Mock).mockRestore(); - (useInitialMetricsQueryState as jest.Mock).mockRestore(); - (useLocalSignedInQueryState as jest.Mock).mockRestore(); - (useProductInfoState as jest.Mock).mockRestore(); - (firefox.requestSignedInUser as jest.Mock).mockRestore(); - (useClientInfoState as jest.Mock).mockRestore(); - (useSession as jest.Mock).mockRestore(); + (useIntegration as jest.Mock).mockReset(); + (useInitialMetricsQueryState as jest.Mock).mockReset(); + (useLocalSignedInQueryState as jest.Mock).mockReset(); + (useProductInfoState as jest.Mock).mockReset(); + (firefox.requestSignedInUser as jest.Mock).mockReset(); + (useClientInfoState as jest.Mock).mockReset(); + (useSession as jest.Mock).mockReset(); mockSessionStatus.mockClear(); }); @@ -483,22 +492,15 @@ describe('SettingsRoutes', () => { }, }); - let navigateResult: Promise; - await act(async () => { - const { - history: { navigate }, - } = renderWithRouter( - - - , - { route: '/' } - ); - navigateResult = navigate(settingsPath); - }); - - await act(() => navigateResult); + const { router } = renderWithRouter( + + + , + { route: '/' } + ); + await router.navigate(settingsPath); await waitFor(() => { expect(ReactUtils.hardNavigate).toHaveBeenCalledWith( @@ -530,22 +532,15 @@ describe('SettingsRoutes', () => { } ); - let navigateResult: Promise; - await act(async () => { - const { - history: { navigate }, - } = renderWithRouter( - - - , - { route: '/' } - ); - navigateResult = navigate(settingsPath); - }); - - await act(() => navigateResult); + const { router } = renderWithRouter( + + + , + { route: '/' } + ); + await router.navigate(settingsPath); await waitFor(() => { expect(ReactUtils.hardNavigate).not.toHaveBeenCalled(); @@ -585,29 +580,22 @@ describe('SettingsRoutes', () => { isSessionVerified: () => Promise.resolve(true), }); - let navigateResult: Promise; - await act(async () => { - const { - history: { navigate }, - } = renderWithRouter( - - - , - { route: '/' } - ); - navigateResult = navigate(settingsPath); - }); - - await act(() => navigateResult); + const { router } = renderWithRouter( + + + , + { route: '/' } + ); + await router.navigate(settingsPath); await waitFor(() => { expect(ReactUtils.hardNavigate).not.toHaveBeenCalled(); @@ -620,26 +608,19 @@ describe('SettingsRoutes', () => { data: { isSignedIn: true }, }); - let navigateResult: Promise; - await act(async () => { - const { - history: { navigate }, - } = renderWithRouter( - - - , - { route: '/' } - ); - navigateResult = navigate(settingsPath); - }); - - await act(() => navigateResult); + const { router } = renderWithRouter( + + + , + { route: '/' } + ); + await router.navigate(settingsPath); await waitFor(() => { expect(ReactUtils.hardNavigate).not.toHaveBeenCalled(); @@ -667,11 +648,11 @@ describe('Integration serviceName error handling', () => { }); afterEach(() => { - (useFxAStatus as jest.Mock).mockRestore(); - (useInitialMetricsQueryState as jest.Mock).mockRestore(); - (useLocalSignedInQueryState as jest.Mock).mockRestore(); - (useSession as jest.Mock).mockRestore(); - (currentAccount as jest.Mock).mockRestore(); + (useFxAStatus as jest.Mock).mockReset(); + (useInitialMetricsQueryState as jest.Mock).mockReset(); + (useLocalSignedInQueryState as jest.Mock).mockReset(); + (useSession as jest.Mock).mockReset(); + (currentAccount as jest.Mock).mockReset(); (sentryMetrics.captureException as jest.Mock).mockClear(); }); @@ -695,11 +676,13 @@ describe('Integration serviceName error handling', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -737,11 +720,13 @@ describe('Integration serviceName error handling', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); @@ -771,17 +756,26 @@ describe('Integration serviceName error handling', () => { .spyOn(console, 'error') .mockImplementation(() => {}); - await expect( - act(async () => { - renderWithLocalizationProvider( - - - - ); - }) - ).rejects.toThrow('Non-OAuth integration error'); + // Use renderWithRouter (createMemoryRouter + RouterProvider) so the + // data router's built-in error boundary catches the thrown error. + renderWithRouter( + + + , + { route: '/' } + ); + + // In react-router v6, thrown errors during render are caught by the + // router's built-in error boundary rather than propagating as a + // rejected promise. Verify the error surfaces in the UI. + expect( + screen.getByText('Unexpected Application Error!') + ).toBeInTheDocument(); + expect( + screen.getByText('Non-OAuth integration error') + ).toBeInTheDocument(); consoleSpy.mockRestore(); }); @@ -806,11 +800,13 @@ describe('Integration serviceName error handling', () => { await act(async () => { renderWithLocalizationProvider( - - - + + + + + ); }); diff --git a/packages/fxa-settings/src/components/App/index.tsx b/packages/fxa-settings/src/components/App/index.tsx index e93b5f38976..1d3de4c35fb 100644 --- a/packages/fxa-settings/src/components/App/index.tsx +++ b/packages/fxa-settings/src/components/App/index.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { RouteComponentProps, Router, useLocation } from '@reach/router'; +import { Routes, Route, useLocation } from 'react-router'; import { lazy, Suspense, @@ -192,7 +192,7 @@ const Settings = lazy(() => import('../Settings')); export const App = ({ flowQueryParams, -}: { flowQueryParams: QueryParams } & RouteComponentProps) => { +}: { flowQueryParams: QueryParams }) => { const { data: isSignedInData } = useLocalSignedInQueryState(); // Configure Sentry before any other hooks that might throw. @@ -424,22 +424,30 @@ export const App = ({ } > - - - + + } /> - + + } + /> + ); }; @@ -452,7 +460,7 @@ const SettingsRoutes = ({ isSignedIn: boolean; integration: Integration; setCurrentSplitLayout: (value: boolean) => void; -} & RouteComponentProps) => { +}) => { const location = useLocation(); const isSync = integration != null ? integration.isSync() : false; @@ -490,9 +498,8 @@ const SettingsRoutes = ({ return ( - + @@ -513,7 +520,7 @@ const AuthAndAccountSetupRoutes = ({ flowQueryParams: QueryParams; isSignedIntoFirefox: boolean; setCurrentSplitLayout: (value: boolean) => void; -} & RouteComponentProps) => { +}) => { const localAccount = currentAccount(); // TODO: MozServices / string discrepancy, FXA-6802 let serviceName: MozServices; @@ -547,319 +554,175 @@ const AuthAndAccountSetupRoutes = ({ return ( <> - + {/* Index */} - - + + } /> + + } /> {/* Legal */} - - - - - + } /> + } /> + } /> + } /> + } /> {/* Other */} - - - + } /> + } /> + } /> {/* Post verify */} - - + + } /> + + } /> {/* Legacy URL kept alive for in-flight redirects and bookmarks from the third-party-auth flow; all new navigations target /post_verify/set_password (FXA-13475). */} - - + + } /> + + } /> {/* Reset password */} - - - - - - - - - - - + + } /> + + } /> + } /> + + } /> + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> {/* Signin */} - - - - - - - - - - - - - - - - - - - - - + } /> + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + } /> + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + } /> + + } /> + + } /> + + } /> + + } /> + + } /> {/* Signup */} - - - - - - - - - - - + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + + } /> + + } /> {/* Pairing */} - - - - - - - - - - - - - - + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* This must be placed after the routes so it's rendered at the bottom of the DOM. */} ({ jest.mock('@react-pdf/renderer', () => { return { pdf: jest.fn().mockResolvedValue({ - toBlob: jest.fn().mockResolvedValue(new Blob()), + toBlob: jest.fn().mockResolvedValue(new globalThis.Blob()), updateContainer: jest.fn(), }), }; @@ -34,10 +35,12 @@ beforeAll(() => { describe('ButtonDownloadRecoveryKeyPDF', () => { it('renders button as expected', () => { const { container } = renderWithLocalizationProvider( - + + + ); screen.getByText('Download and continue'); expect(container).toMatchSnapshot('without CMS'); @@ -47,10 +50,12 @@ describe('ButtonDownloadRecoveryKeyPDF', () => { // including validating that the expected key is included and matches the key in the DataBlock it('emits a metrics event when the link is clicked', () => { renderWithLocalizationProvider( - + + + ); const downloadButton = screen.getByText('Download and continue'); fireEvent.click(downloadButton); diff --git a/packages/fxa-settings/src/components/ErrorBoundaries/index.stories.tsx b/packages/fxa-settings/src/components/ErrorBoundaries/index.stories.tsx index 3616433e41c..fe2d4503d0c 100644 --- a/packages/fxa-settings/src/components/ErrorBoundaries/index.stories.tsx +++ b/packages/fxa-settings/src/components/ErrorBoundaries/index.stories.tsx @@ -8,7 +8,7 @@ import { withLocalization } from 'fxa-react/lib/storybooks'; import { Meta } from '@storybook/react'; import { OAuthQueryParams } from '../../models/pages/signin'; -import { ReachRouterWindow } from '../../lib/window'; +import { RouterWindow } from '../../lib/window'; export default { title: 'Components/AppError', @@ -29,7 +29,7 @@ export const QueryParamValidationError = () => { search: '', }, navigate: function () {}, - } as unknown as ReachRouterWindow) + } as unknown as RouterWindow) ); model.validate(); } catch (error) { diff --git a/packages/fxa-settings/src/components/FormPassword/index.stories.tsx b/packages/fxa-settings/src/components/FormPassword/index.stories.tsx index de32de83ebc..6d426f8a7fc 100644 --- a/packages/fxa-settings/src/components/FormPassword/index.stories.tsx +++ b/packages/fxa-settings/src/components/FormPassword/index.stories.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { Subject } from './mocks'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import FormPassword from '.'; import { Meta } from '@storybook/react'; import { withLocalization, withLocation } from 'fxa-react/lib/storybooks'; @@ -15,21 +15,21 @@ export default { decorators: [withLocalization, withLocation('/settings/password')], } as Meta; export const WithCurrentPassword = () => ( - +
-
+ ); export const WithoutCurrentPassword = () => ( - +
-
+ ); diff --git a/packages/fxa-settings/src/components/FormPassword/index.test.tsx b/packages/fxa-settings/src/components/FormPassword/index.test.tsx index 4513a7ddc34..40efdb0dbec 100644 --- a/packages/fxa-settings/src/components/FormPassword/index.test.tsx +++ b/packages/fxa-settings/src/components/FormPassword/index.test.tsx @@ -16,8 +16,8 @@ export const inputCurrentPassword = typeByTestIdFn( ); const mockNavigate = jest.fn(); -jest.mock('@reach/router', () => ({ - ...jest.requireActual('@reach/router'), +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), useNavigate: () => mockNavigate, })); diff --git a/packages/fxa-settings/src/components/FormPassword/index.tsx b/packages/fxa-settings/src/components/FormPassword/index.tsx index 13d02ccbd40..1dbfbcb9ccf 100644 --- a/packages/fxa-settings/src/components/FormPassword/index.tsx +++ b/packages/fxa-settings/src/components/FormPassword/index.tsx @@ -13,7 +13,7 @@ import InputPassword from '../InputPassword'; import PasswordValidator from '../../lib/password-validator'; import { SETTINGS_PATH } from '../../constants'; import { logViewEvent, settingsViewName } from '../../lib/metrics'; -import { useNavigate } from '@reach/router'; +import { useNavigate } from 'react-router'; type FormPasswordProps = { formState: UseFormMethods['formState']; diff --git a/packages/fxa-settings/src/components/FormPhoneNumber/index.test.tsx b/packages/fxa-settings/src/components/FormPhoneNumber/index.test.tsx index 2e2de9ee529..0b8efadee2f 100644 --- a/packages/fxa-settings/src/components/FormPhoneNumber/index.test.tsx +++ b/packages/fxa-settings/src/components/FormPhoneNumber/index.test.tsx @@ -6,6 +6,7 @@ import { screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import FormPhoneNumber from '.'; import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; +import { MemoryRouter } from 'react-router'; const mockSubmit = jest.fn(); @@ -13,10 +14,12 @@ describe('FormPhoneNumber', () => { async function render() { await act(() => { renderWithLocalizationProvider( - + + + ); }); } @@ -24,10 +27,11 @@ describe('FormPhoneNumber', () => { async function renderWithInfoBannerProps() { await act(async () => { renderWithLocalizationProvider( - + { path: '#', }} /> + ); }); } diff --git a/packages/fxa-settings/src/components/LegalWithMarkdown/index.test.tsx b/packages/fxa-settings/src/components/LegalWithMarkdown/index.test.tsx index c778cbfb90f..b62fed7fbbb 100644 --- a/packages/fxa-settings/src/components/LegalWithMarkdown/index.test.tsx +++ b/packages/fxa-settings/src/components/LegalWithMarkdown/index.test.tsx @@ -6,15 +6,16 @@ import { Subject } from './mocks'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; import { fetchLegalMd } from '../../lib/file-utils-legal'; -import { navigate } from '@reach/router'; jest.mock('../../lib/file-utils-legal'); jest.mock('../../lib/metrics', () => ({ usePageViewEvent: jest.fn(), logViewEvent: jest.fn(), })); -jest.mock('@reach/router', () => ({ - navigate: jest.fn(), +const mockNavigate = jest.fn(); +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), + useNavigate: () => mockNavigate, })); // There's not a good way to use react-markdown in tests until we use jest ESM. Using the jest @@ -40,7 +41,7 @@ describe('LegalWithMarkdown', () => { renderWithLocalizationProvider(); fireEvent.click(screen.getByRole('button', { name: 'Back' })); await waitFor(() => { - expect(navigate).toHaveBeenCalledWith(-1); + expect(mockNavigate).toHaveBeenCalledWith(-1); }); }); diff --git a/packages/fxa-settings/src/components/LegalWithMarkdown/index.tsx b/packages/fxa-settings/src/components/LegalWithMarkdown/index.tsx index 492849ad68d..855aa62927e 100644 --- a/packages/fxa-settings/src/components/LegalWithMarkdown/index.tsx +++ b/packages/fxa-settings/src/components/LegalWithMarkdown/index.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react'; import AppLayout from '../AppLayout'; -import { navigate } from '@reach/router'; +import { useNavigate } from 'react-router'; import { FtlMsg } from 'fxa-react/lib/utils'; import { logViewEvent, usePageViewEvent } from '../../lib/metrics'; import CardHeader from '../CardHeader'; @@ -39,6 +39,7 @@ const LegalWithMarkdown = ({ fetchLegalDoc, }: LegalWithMarkdownProps) => { usePageViewEvent(viewName, REACT_ENTRYPOINT); + const navigate = useNavigate(); const [markdown, setMarkdown] = useState(); const [error, setError] = useState(); const ftlMsgResolver = useFtlMsgResolver(); diff --git a/packages/fxa-settings/src/components/LinkExpired/index.stories.tsx b/packages/fxa-settings/src/components/LinkExpired/index.stories.tsx index 993fa8bbe72..d5adcc7322e 100644 --- a/packages/fxa-settings/src/components/LinkExpired/index.stories.tsx +++ b/packages/fxa-settings/src/components/LinkExpired/index.stories.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { StoryFn } from '@storybook/react'; import { withLocalization } from 'fxa-react/lib/storybooks'; import { LinkExpired, LinkExpiredProps } from '.'; @@ -14,9 +14,9 @@ const meta = { decorators: [ withLocalization, (Story: StoryFn) => ( - + - + ), ], }; diff --git a/packages/fxa-settings/src/components/LinkRememberPassword/index.stories.tsx b/packages/fxa-settings/src/components/LinkRememberPassword/index.stories.tsx index 42a7b5bfa66..69d3b389e09 100644 --- a/packages/fxa-settings/src/components/LinkRememberPassword/index.stories.tsx +++ b/packages/fxa-settings/src/components/LinkRememberPassword/index.stories.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import LinkRememberPassword from '.'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { Meta } from '@storybook/react'; import { MOCK_ACCOUNT } from '../../models/mocks'; import { withLocalization } from 'fxa-react/lib/storybooks'; @@ -15,7 +15,7 @@ export default { } as Meta; export const Default = () => ( - + - + ); diff --git a/packages/fxa-settings/src/components/LinkRememberPassword/index.test.tsx b/packages/fxa-settings/src/components/LinkRememberPassword/index.test.tsx index 98bfa7b357f..4c76184d052 100644 --- a/packages/fxa-settings/src/components/LinkRememberPassword/index.test.tsx +++ b/packages/fxa-settings/src/components/LinkRememberPassword/index.test.tsx @@ -8,12 +8,9 @@ import LinkRememberPassword, { LinkRememberPasswordProps } from '.'; import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils'; import { FluentBundle } from '@fluent/bundle'; import { MOCK_CLIENT_ID, MOCK_EMAIL } from '../../pages/mocks'; -import { - createHistory, - createMemorySource, - LocationProvider, -} from '@reach/router'; +import { MemoryRouter } from 'react-router'; import userEvent from '@testing-library/user-event'; +import { renderWithRouter } from '../../models/mocks'; jest.mock('../../lib/glean', () => ({ __esModule: true, @@ -24,24 +21,18 @@ jest.mock('../../lib/glean', () => ({ }, })); -const mockLocation = () => { - return { - search: '?' + new URLSearchParams({ client_id: MOCK_CLIENT_ID }), - }; +const INITIAL_ROUTE = { + pathname: '/some-start-route', + search: `?client_id=${MOCK_CLIENT_ID}`, }; -jest.mock('@reach/router', () => ({ - ...jest.requireActual('@reach/router'), - useLocation: () => mockLocation(), -})); - const Subject = ({ email = MOCK_EMAIL, clickHandler, }: Partial) => ( - + - + ); describe('LinkRememberPassword', () => { @@ -86,20 +77,11 @@ describe('LinkRememberPassword', () => { }); describe('location state', () => { - beforeEach(() => { - jest.unmock('@reach/router'); - }); - it('updates location with expected state when the link is clicked', async () => { - // Create a custom memory source and history for testing navigation. - const source = createMemorySource('/some-start-route'); - const history = createHistory(source); - - // Render the component within the LocationProvider that uses custom history. - renderWithLocalizationProvider( - - - + // Render the component within a router for testing navigation. + const { router } = renderWithRouter( + , + { route: INITIAL_ROUTE } ); // Find and assert the link is visible. @@ -116,9 +98,9 @@ describe('LinkRememberPassword', () => { await waitFor(() => user.click(rememberPasswordLink)); // Check that the pathname is updated if the link has a destination change. - expect(history.location.pathname).toStrictEqual('/'); + expect(router.state.location.pathname).toStrictEqual('/'); - expect(history.location.state).toMatchObject({ + expect(router.state.location.state).toMatchObject({ prefillEmail: MOCK_EMAIL, }); }); diff --git a/packages/fxa-settings/src/components/LinkRememberPassword/index.tsx b/packages/fxa-settings/src/components/LinkRememberPassword/index.tsx index 90a386fc52e..f745eb80ba5 100644 --- a/packages/fxa-settings/src/components/LinkRememberPassword/index.tsx +++ b/packages/fxa-settings/src/components/LinkRememberPassword/index.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { FtlMsg } from 'fxa-react/lib/utils'; import { isEmailValid } from 'fxa-shared/email/helpers'; -import { Link, useLocation } from '@reach/router'; +import { Link, useLocation } from 'react-router'; export type LinkRememberPasswordProps = { email?: string; diff --git a/packages/fxa-settings/src/components/PromoQrMobile/index.stories.tsx b/packages/fxa-settings/src/components/PromoQrMobile/index.stories.tsx index 0a3c2a0f3ab..000702d63a8 100644 --- a/packages/fxa-settings/src/components/PromoQrMobile/index.stories.tsx +++ b/packages/fxa-settings/src/components/PromoQrMobile/index.stories.tsx @@ -3,11 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { Meta } from '@storybook/react'; -import { - LocationProvider, - createHistory, - createMemorySource, -} from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { withLocalization } from 'fxa-react/lib/storybooks'; import { IntegrationType } from '../../models/integrations'; import { PromoQrMobile } from '.'; @@ -20,22 +16,20 @@ export default { } as Meta; export const Default = () => { - const history = createHistory(createMemorySource('/')); - + return ( - + false }} /> - + ); }; export const WithCardAppLayout = () => { - const history = createHistory(createMemorySource('/')); - + return ( - +

Sign in

Continue to account settings

@@ -43,21 +37,19 @@ export const WithCardAppLayout = () => { false }} /> -
+ ); }; export const DesktopSync = () => { - const history = createHistory(createMemorySource('/')); - return ( - + true, }} /> - + ); }; diff --git a/packages/fxa-settings/src/components/PromoQrMobile/index.test.tsx b/packages/fxa-settings/src/components/PromoQrMobile/index.test.tsx index 87d59751e8f..ff38c1cdda7 100644 --- a/packages/fxa-settings/src/components/PromoQrMobile/index.test.tsx +++ b/packages/fxa-settings/src/components/PromoQrMobile/index.test.tsx @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { screen } from '@testing-library/react'; -import { createHistory, createMemorySource } from '@reach/router'; import { PromoQrMobile, PromoQrMobileIntegration } from '.'; import { IntegrationType } from '../../models/integrations'; import { renderWithRouter } from '../../models/mocks'; @@ -38,9 +37,8 @@ function renderAtRoute( pathname: string, integration: PromoQrMobileIntegration ) { - const history = createHistory(createMemorySource(pathname)); return renderWithRouter(, { - history, + route: pathname, }); } @@ -153,13 +151,12 @@ describe('PromoQrMobile', () => { }); it('uses the CMS-provided QR image URL when the prop is set', () => { - const history = createHistory(createMemorySource('/')); - renderWithRouter( + renderWithRouter( , - { history } + { route: '/' } ); const img = screen.getByAltText( @@ -175,13 +172,12 @@ describe('PromoQrMobile', () => { 'not-a-url', '', ])('ignores invalid CMS URLs (%s) and falls back to local SVG', (url) => { - const history = createHistory(createMemorySource('/')); - renderWithRouter( + renderWithRouter( , - { history } + { route: '/' } ); const img = screen.getByAltText( diff --git a/packages/fxa-settings/src/components/PromoQrMobile/index.tsx b/packages/fxa-settings/src/components/PromoQrMobile/index.tsx index 6bd692e7c9e..4fdc87725ae 100644 --- a/packages/fxa-settings/src/components/PromoQrMobile/index.tsx +++ b/packages/fxa-settings/src/components/PromoQrMobile/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useEffect, useRef } from 'react'; -import { useLocation } from '@reach/router'; +import { useLocation } from 'react-router'; import { FtlMsg } from 'fxa-react/lib/utils'; import ffLogo from '@fxa/shared/assets/images/ff-logo.svg'; import qrMobileKitSrc from './qr-mobile-kit.svg'; diff --git a/packages/fxa-settings/src/components/PromotionBanner/index.stories.tsx b/packages/fxa-settings/src/components/PromotionBanner/index.stories.tsx index fb2a6d56423..8c206b5442e 100644 --- a/packages/fxa-settings/src/components/PromotionBanner/index.stories.tsx +++ b/packages/fxa-settings/src/components/PromotionBanner/index.stories.tsx @@ -8,7 +8,7 @@ import PromotionBanner, { AccountRecoveryKeyPromoBanner, RecoveryPhonePromoBanner, } from '.'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; export default { title: 'Components/PromotionBanner', @@ -17,9 +17,9 @@ export default { decorators: [ withLocalization, (Story) => ( - + - + ), ], } as Meta; diff --git a/packages/fxa-settings/src/components/PromotionBanner/index.test.tsx b/packages/fxa-settings/src/components/PromotionBanner/index.test.tsx index 861f6f1e4c5..dd520b72ff4 100644 --- a/packages/fxa-settings/src/components/PromotionBanner/index.test.tsx +++ b/packages/fxa-settings/src/components/PromotionBanner/index.test.tsx @@ -10,9 +10,10 @@ import PromotionBanner, { } from '.'; import keyImage from './key.svg'; import GleanMetrics from '../../lib/glean'; +import { MemoryRouter } from 'react-router'; -jest.mock('@reach/router', () => ({ - ...jest.requireActual('@reach/router'), +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), useLocation: () => { return { pathname: '/settings', @@ -37,7 +38,7 @@ describe('PromotionBanner component', () => { dismissKey: 'account-recovery-dismissed', metricsKey: 'create_recovery_key', }; - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); screen.getByText('Create'); screen.getByText('Don’t lose your data if you forget your password'); screen.getByText( @@ -56,7 +57,7 @@ describe('PromotionBanner component', () => { dismissKey: 'account-recovery-dismissed', metricsKey: 'create_recovery_key', }; - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); const cta = screen.queryByRole('link', { name: 'Create' }); @@ -77,7 +78,7 @@ describe('PromotionBanner component', () => { describe('recovery key promotion banner', () => { it('renders as expected', () => { - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); expect( screen.getByText('Don’t lose your data if you forget your password') ).toBeVisible(); @@ -101,7 +102,7 @@ describe('PromotionBanner component', () => { GleanMetrics.accountBanner, 'createRecoveryKeyView' ); - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); expect(viewSpy).toHaveBeenCalledTimes(1); }); @@ -109,7 +110,7 @@ describe('PromotionBanner component', () => { describe('recovery phone promotion banner', () => { it('renders as expected', () => { - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); expect( screen.getByText( 'Add extra protection to your account with a recovery phone' @@ -143,7 +144,7 @@ describe('PromotionBanner component', () => { GleanMetrics.accountBanner, 'addRecoveryPhoneView' ); - renderWithLocalizationProvider(); + renderWithLocalizationProvider(); expect(viewSpy).toHaveBeenCalledTimes(1); }); diff --git a/packages/fxa-settings/src/components/PromotionBanner/index.tsx b/packages/fxa-settings/src/components/PromotionBanner/index.tsx index 2daab26d885..221f3fbf676 100644 --- a/packages/fxa-settings/src/components/PromotionBanner/index.tsx +++ b/packages/fxa-settings/src/components/PromotionBanner/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useEffect, useRef, useState } from 'react'; -import { Link, useLocation } from '@reach/router'; +import { Link, useLocation } from 'react-router'; import { ReactComponent as IconClose } from '@fxa/shared/assets/images/close.svg'; import { FtlMsg } from 'fxa-react/lib/utils'; import GleanMetrics from '../../lib/glean'; diff --git a/packages/fxa-settings/src/components/Ready/index.tsx b/packages/fxa-settings/src/components/Ready/index.tsx index f610230de98..4242edc15b9 100644 --- a/packages/fxa-settings/src/components/Ready/index.tsx +++ b/packages/fxa-settings/src/components/Ready/index.tsx @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useEffect } from 'react'; -import { navigate, RouteComponentProps } from '@reach/router'; + import { FtlMsg } from 'fxa-react/lib/utils'; import { logViewEvent, usePageViewEvent } from '../../lib/metrics'; import { REACT_ENTRYPOINT } from '../../constants'; @@ -84,7 +84,7 @@ const Ready = ({ serviceName = MozServices.Default, viewName, integration, -}: ReadyProps & RouteComponentProps) => { +}: ReadyProps) => { usePageViewEvent(viewName, REACT_ENTRYPOINT); // TODO: use `integration.isSync()` @@ -107,7 +107,7 @@ const Ready = ({ const eventName = `${viewName}.start-browsing`; logViewEvent(viewName, eventName, REACT_ENTRYPOINT); const FXA_PRODUCT_PAGE_URL = 'https://www.mozilla.org/firefox/accounts'; - navigate(FXA_PRODUCT_PAGE_URL, { replace: true }); + window.location.replace(FXA_PRODUCT_PAGE_URL); }; useEffect(() => { diff --git a/packages/fxa-settings/src/components/ResetPasswordWarning/index.stories.tsx b/packages/fxa-settings/src/components/ResetPasswordWarning/index.stories.tsx index fce11a3507d..80b60171cbf 100644 --- a/packages/fxa-settings/src/components/ResetPasswordWarning/index.stories.tsx +++ b/packages/fxa-settings/src/components/ResetPasswordWarning/index.stories.tsx @@ -7,7 +7,7 @@ import { withLocalization } from 'fxa-react/lib/storybooks'; import ResetPasswordWarning from '.'; import AppLayout from '../AppLayout'; import { createMockLocationState } from './mocks'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; export default { title: 'Components/ResetPasswordWarning', @@ -16,25 +16,25 @@ export default { } as Meta; export const NoRecoveryKeyExists = () => ( - + - + ); export const RecoveryKeyExists = () => ( - + - + ); export const RecoveryKeyUnkown = () => ( - + - + ); diff --git a/packages/fxa-settings/src/components/ResetPasswordWarning/index.test.tsx b/packages/fxa-settings/src/components/ResetPasswordWarning/index.test.tsx index 0df49a730d0..138caba36c6 100644 --- a/packages/fxa-settings/src/components/ResetPasswordWarning/index.test.tsx +++ b/packages/fxa-settings/src/components/ResetPasswordWarning/index.test.tsx @@ -7,11 +7,14 @@ import { screen, waitFor } from '@testing-library/react'; import ResetPasswordWarning from '.'; import userEvent from '@testing-library/user-event'; import { createMockLocationState } from './mocks'; +import { MemoryRouter } from 'react-router'; describe('ResetPasswordWarning component', () => { it('renders as expected when no recovery key exists', async () => { renderWithLocalizationProvider( - + + + ); expect( @@ -66,7 +69,9 @@ describe('ResetPasswordWarning component', () => { it('renders additional message point when recovery key exists', async () => { renderWithLocalizationProvider( - + + + ); expect(screen.getByText('Have an account recovery key?')).toBeVisible(); @@ -79,7 +84,9 @@ describe('ResetPasswordWarning component', () => { it('renders additional message point when recovery key status is undefined', async () => { renderWithLocalizationProvider( - + + + ); expect(screen.getByText('Have an account recovery key?')).toBeVisible(); @@ -90,7 +97,9 @@ describe('ResetPasswordWarning component', () => { global.dispatchEvent(new Event('resize')); renderWithLocalizationProvider( - + + + ); expect( @@ -116,7 +125,9 @@ describe('ResetPasswordWarning component', () => { global.dispatchEvent(new Event('resize')); renderWithLocalizationProvider( - + + + ); user.click(screen.getByRole('img', { name: 'Expand warning' })); @@ -144,10 +155,12 @@ describe('ResetPasswordWarning component', () => { it('defaults to collapsed view if defaultClosed is true', async () => { renderWithLocalizationProvider( - + + + ); expect( diff --git a/packages/fxa-settings/src/components/ResetPasswordWarning/index.tsx b/packages/fxa-settings/src/components/ResetPasswordWarning/index.tsx index 51386742b4b..3c82cb2102a 100644 --- a/packages/fxa-settings/src/components/ResetPasswordWarning/index.tsx +++ b/packages/fxa-settings/src/components/ResetPasswordWarning/index.tsx @@ -11,7 +11,7 @@ import { ReactComponent as Chevron } from './chevron.svg'; import { FtlMsg } from 'fxa-react/lib/utils'; import { useFtlMsgResolver } from '../../models'; -import { Link } from '@reach/router'; +import { Link } from 'react-router'; import { CompleteResetPasswordLocationState } from '../../pages/ResetPassword/CompleteResetPassword/interfaces'; import GleanMetrics from '../../lib/glean'; diff --git a/packages/fxa-settings/src/components/Settings/ConnectAnotherDevicePromo/index.stories.tsx b/packages/fxa-settings/src/components/Settings/ConnectAnotherDevicePromo/index.stories.tsx index de7a17938c2..3a9876ed918 100644 --- a/packages/fxa-settings/src/components/Settings/ConnectAnotherDevicePromo/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/ConnectAnotherDevicePromo/index.stories.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { ConnectAnotherDevicePromo } from '.'; import { Meta } from '@storybook/react'; import { withLocalization } from 'fxa-react/lib/storybooks'; @@ -16,7 +16,7 @@ export default { decorators: [ withLocalization, (Story) => ( - + - + ), ], } as Meta; diff --git a/packages/fxa-settings/src/components/Settings/ConnectedServices/index.stories.tsx b/packages/fxa-settings/src/components/Settings/ConnectedServices/index.stories.tsx index 468a1db2c16..6023b07f587 100644 --- a/packages/fxa-settings/src/components/Settings/ConnectedServices/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/ConnectedServices/index.stories.tsx @@ -4,7 +4,7 @@ import { Meta } from '@storybook/react'; import { withLocalization } from 'fxa-react/lib/storybooks'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { ConnectedServices } from '.'; import { @@ -37,11 +37,11 @@ const storyWithContext = (account: Partial) => { const context = { account: account as Account }; const story = () => ( - + - + ); return story; }; diff --git a/packages/fxa-settings/src/components/Settings/FlowContainer/index.stories.tsx b/packages/fxa-settings/src/components/Settings/FlowContainer/index.stories.tsx index f270870ebb9..c6ac9153a81 100644 --- a/packages/fxa-settings/src/components/Settings/FlowContainer/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowContainer/index.stories.tsx @@ -7,7 +7,7 @@ import { Meta } from '@storybook/react'; import { withLocalization } from 'fxa-react/lib/storybooks'; import { FlowContainer } from '.'; import { ProgressBar } from '../ProgressBar'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { MOCK_CONTENT, MOCK_SUBTITLE, MOCK_TITLE } from './mocks'; export default { @@ -16,9 +16,9 @@ export default { decorators: [ withLocalization, (Story) => ( - + - + ), ], } as Meta; diff --git a/packages/fxa-settings/src/components/Settings/FlowContainer/index.tsx b/packages/fxa-settings/src/components/Settings/FlowContainer/index.tsx index 9a3c5255179..bd2089aa48c 100644 --- a/packages/fxa-settings/src/components/Settings/FlowContainer/index.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowContainer/index.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { useFtlMsgResolver } from '../../../models'; -import { RouteComponentProps } from '@reach/router'; + import Head from 'fxa-react/components/Head'; import ButtonBack from '../../ButtonBack'; import classNames from 'classnames'; @@ -29,7 +29,7 @@ export const FlowContainer = ({ hideBackButton = false, localizedBackButtonTitle, children, -}: FlowContainerProps & RouteComponentProps) => { +}: FlowContainerProps) => { const ftlMsgResolver = useFtlMsgResolver(); const backButtonTitle = localizedBackButtonTitle ? localizedBackButtonTitle diff --git a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyConfirmPwd/index.tsx b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyConfirmPwd/index.tsx index 222724e94ce..bbbb127da52 100644 --- a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyConfirmPwd/index.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyConfirmPwd/index.tsx @@ -13,7 +13,7 @@ import { AuthUiErrors } from '../../../lib/auth-errors/auth-errors'; import InputPassword from '../../InputPassword'; import { PasswordImage } from '../../images'; import { RecoveryKeyAction } from '../PageRecoveryKeyCreate'; -import { Link } from '@reach/router'; +import { Link } from 'react-router'; import { SETTINGS_PATH } from '../../../constants'; import { getLocalizedErrorMessage } from '../../../lib/error-utils'; import { formatRecoveryKey } from '../../../lib/utilities'; diff --git a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyDownload/index.test.tsx b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyDownload/index.test.tsx index f3a2fdd95d7..75364bf7003 100644 --- a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyDownload/index.test.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyDownload/index.test.tsx @@ -23,7 +23,7 @@ jest.mock('../../../lib/metrics', () => ({ jest.mock('@react-pdf/renderer', () => { return { pdf: jest.fn().mockResolvedValue({ - toBlob: jest.fn().mockResolvedValue(new Blob()), + toBlob: jest.fn().mockResolvedValue(new globalThis.Blob()), updateContainer: jest.fn(), }), }; diff --git a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyHint/index.stories.tsx b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyHint/index.stories.tsx index c456ba238c8..86a7f217cdd 100644 --- a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyHint/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyHint/index.stories.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import FlowRecoveryKeyHint from '.'; import { Meta } from '@storybook/react'; import { Account, AppContext } from '../../../models'; @@ -41,7 +41,7 @@ const accountWithUnknownError = { const storyWithContext = (account: Account) => { const story = () => ( - + { }} /> - + ); return story; }; diff --git a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyInfo/index.tsx b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyInfo/index.tsx index f87977be389..a4ea81a73e7 100644 --- a/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyInfo/index.tsx +++ b/packages/fxa-settings/src/components/Settings/FlowRecoveryKeyInfo/index.tsx @@ -10,7 +10,7 @@ import { ShieldIconListItem, KeyIconListItem } from '../../IconListItem'; import { FtlMsg } from 'fxa-react/lib/utils'; import { logViewEvent } from '../../../lib/metrics'; import { RecoveryKeyAction } from '../PageRecoveryKeyCreate'; -import { Link } from '@reach/router'; +import { Link } from 'react-router'; import { SETTINGS_PATH } from '../../../constants'; export type FlowRecoveryKeyInfoProps = { diff --git a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.stories.tsx b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.stories.tsx index 19fa9761c9d..cba885f12fc 100644 --- a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.stories.tsx @@ -7,11 +7,7 @@ import { withLocalization } from 'fxa-react/lib/storybooks'; import { HeaderLockup } from '.'; import { Account, AppContext } from '../../../models'; import { mockAppContext, MOCK_ACCOUNT } from 'fxa-settings/src/models/mocks'; -import { - LocationProvider, - createHistory, - createMemorySource, -} from '@reach/router'; +import { MemoryRouter } from 'react-router'; export default { title: 'Components/Settings/HeaderLockup', @@ -34,15 +30,13 @@ const storyWithContext = ( route: string = '/settings/emails' ) => { const context = { account: account as Account }; - const source = createMemorySource(route); - const history = createHistory(source); - + const story = () => ( - + - + ); return story; }; diff --git a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.test.tsx b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.test.tsx index d1906f8db86..1965e0ff2ce 100644 --- a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.test.tsx +++ b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.test.tsx @@ -7,7 +7,6 @@ import HeaderLockup from '.'; import { userEvent } from '@testing-library/user-event'; import GleanMetrics from '../../../lib/glean'; import { renderWithRouter } from '../../../models/mocks'; -import { createHistory, createMemorySource } from '@reach/router'; jest.mock('../../../lib/glean', () => ({ __esModule: true, @@ -22,7 +21,6 @@ describe('HeaderLockup', () => { it('renders as expected on other settings pages', () => { renderWithRouter(, { route: '/settings/emails', - history: createHistory(createMemorySource('/settings/emails')), }); const headerMenu = screen.getByTestId('header-menu'); @@ -49,7 +47,6 @@ describe('HeaderLockup', () => { it('shows the correct tooltip when at the top-level /settings route', () => { renderWithRouter(, { route: '/settings', - history: createHistory(createMemorySource('/settings')), }); const logo = screen.getByTestId('back-to-settings'); diff --git a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.tsx b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.tsx index 49dd1a71a42..0fa5b9312c3 100644 --- a/packages/fxa-settings/src/components/Settings/HeaderLockup/index.tsx +++ b/packages/fxa-settings/src/components/Settings/HeaderLockup/index.tsx @@ -15,7 +15,7 @@ import { ReactComponent as Menu } from './menu.svg'; import { ReactComponent as Close } from './close.svg'; import Sidebar from '../Sidebar'; import GleanMetrics from '../../../lib/glean'; -import { Link, useLocation } from '@reach/router'; +import { Link, useLocation } from 'react-router'; export const HeaderLockup = () => { const [sidebarRevealedState, setNavState] = useState(false); diff --git a/packages/fxa-settings/src/components/Settings/LinkedAccounts/LinkedAccount.tsx b/packages/fxa-settings/src/components/Settings/LinkedAccounts/LinkedAccount.tsx index c95c2d67d0a..58c39960126 100644 --- a/packages/fxa-settings/src/components/Settings/LinkedAccounts/LinkedAccount.tsx +++ b/packages/fxa-settings/src/components/Settings/LinkedAccounts/LinkedAccount.tsx @@ -8,7 +8,7 @@ import { ReactComponent as AppleIcon } from './apple.svg'; import { Modal } from '../Modal'; import { useAccount, useFtlMsgResolver } from '../../../models'; import { useBooleanState } from 'fxa-react/lib/hooks'; -import { useLocation } from '@reach/router'; +import { useLocation } from 'react-router'; import { useNavigateWithQuery } from '../../../lib/hooks/useNavigateWithQuery'; import { SETTINGS_PATH } from '../../../constants'; import { diff --git a/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.stories.tsx b/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.stories.tsx index 30671ccb7fc..098b76717dc 100644 --- a/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.stories.tsx @@ -9,7 +9,7 @@ import { LinkedAccounts } from '.'; import { MOCK_LINKED_ACCOUNTS } from './mocks'; import { AppContext } from 'fxa-settings/src/models'; import { mockAppContext } from 'fxa-settings/src/models/mocks'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; export default { title: 'Components/Settings/LinkedAccounts', @@ -18,7 +18,7 @@ export default { } as Meta; export const Default = () => ( - + ( > - + ); export const WithPasswordUnlinkModal = () => { @@ -42,7 +42,7 @@ export const WithPasswordUnlinkModal = () => { }, []); return ( - + { > - + ); }; @@ -68,7 +68,7 @@ export const NoPasswordUnlinkModal = () => { }, []); return ( - + { > - + ); }; diff --git a/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.test.tsx b/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.test.tsx index 6b3116ce651..423e2258190 100644 --- a/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.test.tsx +++ b/packages/fxa-settings/src/components/Settings/LinkedAccounts/index.test.tsx @@ -30,8 +30,8 @@ const MOCK_ACCOUNT = { } as unknown as Account; const mockNavigate = jest.fn(); -jest.mock('@reach/router', () => ({ - ...jest.requireActual('@reach/router'), +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), useNavigate: () => mockNavigate, useLocation: () => mockLocation(), })); diff --git a/packages/fxa-settings/src/components/Settings/MfaGuard/index.test.tsx b/packages/fxa-settings/src/components/Settings/MfaGuard/index.test.tsx index 77c4c7b0d79..07ae5ae7a56 100644 --- a/packages/fxa-settings/src/components/Settings/MfaGuard/index.test.tsx +++ b/packages/fxa-settings/src/components/Settings/MfaGuard/index.test.tsx @@ -42,8 +42,8 @@ jest.mock('../../../models', () => ({ useAlertBar: () => mockAlertBar, })); -jest.mock('@reach/router', () => ({ - ...jest.requireActual('@reach/router'), +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), useNavigate: () => mockNavigate, })); diff --git a/packages/fxa-settings/src/components/Settings/MfaGuard/index.tsx b/packages/fxa-settings/src/components/Settings/MfaGuard/index.tsx index b085ad59c0c..9d26d5190dd 100644 --- a/packages/fxa-settings/src/components/Settings/MfaGuard/index.tsx +++ b/packages/fxa-settings/src/components/Settings/MfaGuard/index.tsx @@ -27,7 +27,7 @@ import { } from '../../../lib/cache'; import { MfaReason, MfaScope } from '../../../lib/types'; import { ERRNO } from '@fxa/accounts/errors'; -import { useNavigate } from '@reach/router'; +import { useNavigate } from 'react-router'; import * as Sentry from '@sentry/react'; import { getLocalizedErrorMessage } from '../../../lib/error-utils'; import GleanMetrics from '../../../lib/glean'; diff --git a/packages/fxa-settings/src/components/Settings/Modal/index.stories.tsx b/packages/fxa-settings/src/components/Settings/Modal/index.stories.tsx index e8e0b3ec964..c1ff661c4a6 100644 --- a/packages/fxa-settings/src/components/Settings/Modal/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/Modal/index.stories.tsx @@ -7,7 +7,7 @@ import { Meta } from '@storybook/react'; import { withLocalization } from 'fxa-react/lib/storybooks'; import { useBooleanState } from 'fxa-react/lib/hooks'; import { Modal } from '.'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; // ModalToggle only included for Storybook functionality type ModalToggleChildrenProps = { @@ -43,9 +43,9 @@ export default { decorators: [ withLocalization, (Story) => ( - + - + ), ], } as Meta; diff --git a/packages/fxa-settings/src/components/Settings/Modal/index.tsx b/packages/fxa-settings/src/components/Settings/Modal/index.tsx index 5851cca2bec..c2191b332ba 100644 --- a/packages/fxa-settings/src/components/Settings/Modal/index.tsx +++ b/packages/fxa-settings/src/components/Settings/Modal/index.tsx @@ -8,7 +8,7 @@ import { useEscKeydownEffect, useChangeFocusEffect } from '../../../lib/hooks'; import classNames from 'classnames'; import Portal from 'fxa-react/components/Portal'; import { ReactComponent as CloseIcon } from '@fxa/shared/assets/images/close.svg'; -import { Link, useLocation } from '@reach/router'; +import { Link, useLocation } from 'react-router'; import { useFtlMsgResolver } from '../../../models'; import { FtlMsg } from 'fxa-react/lib/utils'; import { GleanClickEventDataAttrs } from '../../../lib/types'; diff --git a/packages/fxa-settings/src/components/Settings/ModalVerifySession/index.stories.tsx b/packages/fxa-settings/src/components/Settings/ModalVerifySession/index.stories.tsx index 666e21010b8..366f5b87796 100644 --- a/packages/fxa-settings/src/components/Settings/ModalVerifySession/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/ModalVerifySession/index.stories.tsx @@ -9,7 +9,7 @@ import { useBooleanState } from 'fxa-react/lib/hooks'; import { ModalVerifySession } from '.'; import { AppContext } from 'fxa-settings/src/models'; import { mockSession, MOCK_ACCOUNT } from 'fxa-settings/src/models/mocks'; -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; export default { title: 'Components/Settings/ModalVerifySession', @@ -49,7 +49,7 @@ const ModalToggle = ({ children }: ModalToggleProps) => { }; export const DefaultWithValidCode123456 = () => ( - + {({ modalRevealed, hideModal }) => @@ -63,5 +63,5 @@ export const DefaultWithValidCode123456 = () => ( } - + ); diff --git a/packages/fxa-settings/src/components/Settings/Nav/index.stories.tsx b/packages/fxa-settings/src/components/Settings/Nav/index.stories.tsx index 0c12ef41493..43bfae650dc 100644 --- a/packages/fxa-settings/src/components/Settings/Nav/index.stories.tsx +++ b/packages/fxa-settings/src/components/Settings/Nav/index.stories.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LocationProvider } from '@reach/router'; +import { MemoryRouter } from 'react-router'; import { getDefault, Config } from '../../../lib/config'; import { Nav } from '.'; import { AppContext } from 'fxa-settings/src/models'; @@ -46,11 +46,11 @@ const storyWithContext = ( : { account: account as Account }; const story = () => ( - +