diff --git a/package.json b/package.json
index c32f6c8fcd..2cba4f0071 100644
--- a/package.json
+++ b/package.json
@@ -89,8 +89,8 @@
"react-markdown": "^5.0.3",
"react-picky": "^5.3.2",
"react-redux": "^5.0.5",
- "react-router": "^4.1.1",
- "react-router-dom": "^4.1.1",
+ "react-router": "^7.0.0",
+ "react-router-dom": "^7.0.0",
"react-select": "^3.0.4",
"react-test-renderer": "^16.8.4",
"react-zendesk": "^0.1.5",
diff --git a/static/js/Router.js b/static/js/Router.js
index c4f6861ec3..ee4583de76 100644
--- a/static/js/Router.js
+++ b/static/js/Router.js
@@ -1,30 +1,31 @@
import React from "react"
-import { Route, Router as ReactRouter } from "react-router"
+import { BrowserRouter, Route } from "react-router-dom"
import { Provider } from "react-redux"
import App from "./containers/App"
import withTracker from "./util/withTracker"
import ScrollToTop from "./components/ScrollToTop"
+const TrackedApp = withTracker(App);
+
export default class Root extends React.Component {
props: {
- history: Object,
store: Store
}
render() {
- const { children, history, store } = this.props
+ const { children, store } = this.props
return (
)
}
}
-export const routes =
+export const routes = } />
diff --git a/static/js/components/PrivateRoute.js b/static/js/components/PrivateRoute.js
index f94b696717..6bafdb4693 100644
--- a/static/js/components/PrivateRoute.js
+++ b/static/js/components/PrivateRoute.js
@@ -1,6 +1,6 @@
// @flow
import React, { type ComponentType } from "react";
-import { Route, Redirect } from "react-router-dom";
+import { Route, Navigate } from "react-router-dom";
import { connect } from "react-redux";
import { compose } from "redux";
import { createStructuredSelector } from "reselect";
@@ -28,7 +28,7 @@ export const PrivateRoute = ({
return currentUser && currentUser.is_authenticated ? (
) : (
-
+
);
}}
/>
diff --git a/static/js/components/PrivateRoute_test.js b/static/js/components/PrivateRoute_test.js
index 9e0509d31e..b1004b659e 100644
--- a/static/js/components/PrivateRoute_test.js
+++ b/static/js/components/PrivateRoute_test.js
@@ -1,7 +1,7 @@
// @flow
import React from "react";
import { assert } from "chai";
-import { Redirect } from "react-router-dom";
+import { Navigate } from "react-router-dom";
import PrivateRoute, {
PrivateRoute as InnerPrivateRoute,
@@ -56,7 +56,7 @@ describe("PrivateRoute component", () => {
assert.equal(path, fakeRoutePath);
const renderResult = render();
if (isAnonymous) {
- assert.equal(renderResult.type, Redirect);
+ assert.equal(renderResult.type, Navigate);
assert.equal(
renderResult.props.to,
`${routes.login.begin}?next=%2Fprotected%2Froute`,
diff --git a/static/js/components/ScrollToTop.js b/static/js/components/ScrollToTop.js
index e977b48959..4d9fdf06e1 100644
--- a/static/js/components/ScrollToTop.js
+++ b/static/js/components/ScrollToTop.js
@@ -1,16 +1,12 @@
-import React from "react";
-import { withRouter } from "react-router";
+import React, { useEffect } from "react";
+import { useLocation } from "react-router-dom";
-class ScrollToTop extends React.Component {
- componentDidUpdate(prevProps) {
- if (this.props.location.pathname !== prevProps.location.pathname) {
- window.scrollTo(0, 0);
- }
- }
-
- render() {
- return this.props.children;
- }
+function ScrollToTop({ children }) {
+ const location = useLocation();
+ useEffect(() => {
+ window.scrollTo(0, 0);
+ }, [location.pathname]);
+ return children;
}
-export default withRouter(ScrollToTop);
+export default ScrollToTop;
diff --git a/static/js/containers/App.js b/static/js/containers/App.js
index 57f805c883..fa57d2b981 100644
--- a/static/js/containers/App.js
+++ b/static/js/containers/App.js
@@ -3,7 +3,7 @@
import React from "react";
import { compose } from "redux";
import { connect } from "react-redux";
-import { Switch, Route } from "react-router";
+import { Routes, Route } from "react-router-dom";
import { connectRequest } from "redux-query";
import { createStructuredSelector } from "reselect";
import urljoin from "url-join";
@@ -84,49 +84,76 @@ export class App extends React.Component {
errorPageHeader={null}
courseTopics={courseTopics}
/>
-
-
+
+
+
+ }
/>
-
+
+
+ }
/>
}
/>
}
/>
-
+
+
+ }
/>
+
+
+ }
/>
+
+
+ }
/>
+
+
+ }
/>
-
+
+
+ }
/>
}
/>
-
+
);
}
diff --git a/static/js/containers/pages/admin/EcommerceAdminPages.js b/static/js/containers/pages/admin/EcommerceAdminPages.js
index 5dadaa172d..cb52594a46 100644
--- a/static/js/containers/pages/admin/EcommerceAdminPages.js
+++ b/static/js/containers/pages/admin/EcommerceAdminPages.js
@@ -5,7 +5,7 @@ declare var USER_PERMISSIONS: {
has_coupon_product_assignment_permission: boolean,
};
import React from "react";
-import { Redirect, Route, Switch, Link } from "react-router-dom";
+import { Navigate, Route, Routes, Link } from "react-router-dom";
import { routes } from "../../../lib/urls";
@@ -50,34 +50,34 @@ const EcommerceAdminIndexPage = () => (
const EcommerceAdminPages = () => (
-
+
}
/>
}
/>
}
/>
}
/>
}
/>
-
-
+
+
);
diff --git a/static/js/containers/pages/b2b/EcommerceBulkPages.js b/static/js/containers/pages/b2b/EcommerceBulkPages.js
index 680bf7de1a..6c9cd25bd7 100644
--- a/static/js/containers/pages/b2b/EcommerceBulkPages.js
+++ b/static/js/containers/pages/b2b/EcommerceBulkPages.js
@@ -1,6 +1,6 @@
// @flow
import React from "react";
-import { Route, Switch } from "react-router-dom";
+import { Routes, Route } from "react-router-dom";
import { routes } from "../../../lib/urls";
@@ -9,18 +9,10 @@ import B2BReceiptPage from "./B2BReceiptPage";
const EcommerceBulkPages = () => (
-
-
-
-
+
+ } />
+ } />
+
);
diff --git a/static/js/containers/pages/login/LoginPages.js b/static/js/containers/pages/login/LoginPages.js
index 98e95a4395..cfb4faf136 100644
--- a/static/js/containers/pages/login/LoginPages.js
+++ b/static/js/containers/pages/login/LoginPages.js
@@ -1,6 +1,6 @@
// @flow
import React from "react";
-import { Switch, Route } from "react-router-dom";
+import { Routes, Route } from "react-router-dom";
import { routes } from "../../../lib/urls";
@@ -14,25 +14,31 @@ const ForgotPasswordPages = () => (
}
/>
}
+ />
+ }
/>
);
const LoginPages = () => (
-
-
-
+
+ } />
+ } />
}
/>
-
+
);
export default LoginPages;
diff --git a/static/js/containers/pages/profile/ProfilePages.js b/static/js/containers/pages/profile/ProfilePages.js
index 77e47719f5..4c4207b61d 100644
--- a/static/js/containers/pages/profile/ProfilePages.js
+++ b/static/js/containers/pages/profile/ProfilePages.js
@@ -1,6 +1,6 @@
// @flow
import React from "react";
-import { Switch, Route } from "react-router-dom";
+import { Routes, Route } from "react-router-dom";
import { routes } from "../../../lib/urls";
@@ -8,10 +8,10 @@ import ViewProfilePage from "./ViewProfilePage";
import EditProfilePage from "./EditProfilePage";
const ProfilePages = () => (
-
-
-
-
+
+ } />
+ } />
+
);
export default ProfilePages;
diff --git a/static/js/containers/pages/register/RegisterConfirmPage_test.js b/static/js/containers/pages/register/RegisterConfirmPage_test.js
index add91c9d68..ffec6a7d6c 100644
--- a/static/js/containers/pages/register/RegisterConfirmPage_test.js
+++ b/static/js/containers/pages/register/RegisterConfirmPage_test.js
@@ -82,10 +82,14 @@ describe("RegisterConfirmPage", () => {
});
const confirmationErrorText = inner.find(".confirmation-message");
assert.isNotNull(confirmationErrorText);
- assert.equal(
- confirmationErrorText.text().replace("", ""),
- "This invitation is invalid or has expired. Please .",
+ assert.include(
+ confirmationErrorText.text(),
+ "This invitation is invalid or has expired. Please",
);
+ const link = confirmationErrorText.find("Link");
+ assert.isNotNull(link);
+ assert.equal(link.prop("to"), routes.register);
+ assert.equal(link.text(), "click here to register again");
});
it("Shows a login link with existing account message", async () => {
@@ -102,10 +106,14 @@ describe("RegisterConfirmPage", () => {
});
const confirmationErrorText = inner.find(".confirmation-message");
assert.isNotNull(confirmationErrorText);
- assert.equal(
- confirmationErrorText.text().replace("", ""),
- "You already have an xPRO account. Please .",
+ assert.include(
+ confirmationErrorText.text(),
+ "You already have an xPRO account. Please",
);
+ const link = confirmationErrorText.find("Link");
+ assert.isNotNull(link);
+ assert.equal(link.prop("to"), routes.login);
+ assert.equal(link.text(), "click here to sign in");
});
it("Shows a register link with invalid or no confirmation code", async () => {
@@ -121,9 +129,14 @@ describe("RegisterConfirmPage", () => {
},
});
const confirmationErrorText = inner.find(".confirmation-message");
- assert.equal(
- confirmationErrorText.text().replace("", ""),
- "No confirmation code was provided or it has expired. .",
+ assert.isNotNull(confirmationErrorText);
+ assert.include(
+ confirmationErrorText.text(),
+ "No confirmation code was provided or it has expired.",
);
+ const link = confirmationErrorText.find("Link");
+ assert.isNotNull(link);
+ assert.equal(link.prop("to"), routes.register);
+ assert.equal(link.text(), "click here to register again");
});
});
diff --git a/static/js/containers/pages/register/RegisterDetailsPage_test.js b/static/js/containers/pages/register/RegisterDetailsPage_test.js
index ab42465f6a..db32545f6d 100644
--- a/static/js/containers/pages/register/RegisterDetailsPage_test.js
+++ b/static/js/containers/pages/register/RegisterDetailsPage_test.js
@@ -164,9 +164,13 @@ describe("RegisterDetailsPage", () => {
});
const confirmationMessage = inner.find(".confirmation-message");
assert.isNotNull(confirmationMessage);
- assert.equal(
- confirmationMessage.text().replace("", ""),
- "You already have an xPRO account. Please .",
+ assert.include(
+ confirmationMessage.text(),
+ "You already have an xPRO account. Please",
);
+ const link = confirmationMessage.find("Link");
+ assert.isNotNull(link);
+ assert.equal(link.prop("to"), routes.login);
+ assert.equal(link.text(), "click here to sign in");
});
});
diff --git a/static/js/containers/pages/register/RegisterPages.js b/static/js/containers/pages/register/RegisterPages.js
index 4bac48d260..00f495c0ba 100644
--- a/static/js/containers/pages/register/RegisterPages.js
+++ b/static/js/containers/pages/register/RegisterPages.js
@@ -1,6 +1,6 @@
// @flow
import React from "react";
-import { Switch, Route, Redirect } from "react-router-dom";
+import { Routes, Route, Navigate } from "react-router-dom";
import { routes } from "../../../lib/urls";
@@ -14,36 +14,44 @@ import RegisterErrorPage from "./RegisterErrorPage";
const RegisterPages = () => (
-
-
+
+ }
+ />
}
/>
}
/>
}
/>
}
+ />
+ }
/>
-
}
/>
-
-
+
+
);
diff --git a/yarn.lock b/yarn.lock
index 5b004f8784..fb2e4431ef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4200,6 +4200,13 @@ __metadata:
languageName: node
linkType: hard
+"cookie@npm:^1.0.1":
+ version: 1.0.2
+ resolution: "cookie@npm:1.0.2"
+ checksum: 2c5a6214147ffa7135ce41860c781de17e93128689b0d080d3116468274b3593b607bcd462ac210d3a61f081db3d3b09ae106e18d60b1f529580e95cf2db8a55
+ languageName: node
+ linkType: hard
+
"cookiejar@npm:^2.1.0":
version: 2.1.4
resolution: "cookiejar@npm:2.1.4"
@@ -6790,20 +6797,6 @@ __metadata:
languageName: node
linkType: hard
-"history@npm:^4.7.2":
- version: 4.10.1
- resolution: "history@npm:4.10.1"
- dependencies:
- "@babel/runtime": ^7.1.2
- loose-envify: ^1.2.0
- resolve-pathname: ^3.0.0
- tiny-invariant: ^1.0.2
- tiny-warning: ^1.0.0
- value-equal: ^1.0.1
- checksum: addd84bc4683929bae4400419b5af132ff4e4e9b311a0d4e224579ea8e184a6b80d7f72c55927e4fa117f69076a9e47ce082d8d0b422f1a9ddac7991490ca1d0
- languageName: node
- linkType: hard
-
"history@npm:^5.0.0":
version: 5.3.0
resolution: "history@npm:5.3.0"
@@ -6831,13 +6824,6 @@ __metadata:
languageName: node
linkType: hard
-"hoist-non-react-statics@npm:^2.5.0":
- version: 2.5.5
- resolution: "hoist-non-react-statics@npm:2.5.5"
- checksum: ee2d05e5c7e1398ad84a15b0327f66bd78f38a8e0015e852f954b36434e32eb7e942d5357505020a3a1147f247b165bf1e69d72393e3accab67cafdafeb86230
- languageName: node
- linkType: hard
-
"hoist-non-react-statics@npm:^3.3.0":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
@@ -8398,7 +8384,7 @@ __metadata:
languageName: node
linkType: hard
-"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0":
+"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
@@ -8937,8 +8923,8 @@ __metadata:
react-markdown: ^5.0.3
react-picky: ^5.3.2
react-redux: ^5.0.5
- react-router: ^4.1.1
- react-router-dom: ^4.1.1
+ react-router: ^7.0.0
+ react-router-dom: ^7.0.0
react-select: ^3.0.4
react-test-renderer: ^16.8.4
react-zendesk: ^0.1.5
@@ -10489,36 +10475,31 @@ __metadata:
languageName: node
linkType: hard
-"react-router-dom@npm:^4.1.1":
- version: 4.3.1
- resolution: "react-router-dom@npm:4.3.1"
+"react-router-dom@npm:^7.0.0":
+ version: 7.6.2
+ resolution: "react-router-dom@npm:7.6.2"
dependencies:
- history: ^4.7.2
- invariant: ^2.2.4
- loose-envify: ^1.3.1
- prop-types: ^15.6.1
- react-router: ^4.3.1
- warning: ^4.0.1
+ react-router: 7.6.2
peerDependencies:
- react: ">=15"
- checksum: e73b12fc97d1019a63c6ab5862a491634b8d9e5a44f954b0831913f5853faccab364eac3c1eb3563c74998efeb8675f857a4fa9aa1f6d3b46368557f5a6f935b
+ react: ">=18"
+ react-dom: ">=18"
+ checksum: 944f87d4d62eddbd517b9cbd39c9282200b172b31309cdb0eca3803f768c2b29466d852db7ce4a997ae1818d8103a1d1322e7fca4d6d4d846757b0e00b931c90
languageName: node
linkType: hard
-"react-router@npm:^4.1.1, react-router@npm:^4.3.1":
- version: 4.3.1
- resolution: "react-router@npm:4.3.1"
+"react-router@npm:7.6.2, react-router@npm:^7.0.0":
+ version: 7.6.2
+ resolution: "react-router@npm:7.6.2"
dependencies:
- history: ^4.7.2
- hoist-non-react-statics: ^2.5.0
- invariant: ^2.2.4
- loose-envify: ^1.3.1
- path-to-regexp: ^1.7.0
- prop-types: ^15.6.1
- warning: ^4.0.1
+ cookie: ^1.0.1
+ set-cookie-parser: ^2.6.0
peerDependencies:
- react: ">=15"
- checksum: 144f2167e4589ec1eea3d9d178cf18571ee20bbd4abe8a46518dda91cd28db9da78c11b52cfd92d1bdb1e3d38b9751fa173ed85ced4e43a76f89cacd3502cf88
+ react: ">=18"
+ react-dom: ">=18"
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ checksum: 6d3c931043dcbc3dd16dc431e3a7701403c2a786068968154374d1bbcb7476b8ae7ae9279ed81cc9c36ce537b8d7355c13d126045cd52c98decc3bd1194cc4f9
languageName: node
linkType: hard
@@ -10966,13 +10947,6 @@ __metadata:
languageName: node
linkType: hard
-"resolve-pathname@npm:^3.0.0":
- version: 3.0.0
- resolution: "resolve-pathname@npm:3.0.0"
- checksum: 6147241ba42c423dbe83cb067a2b4af4f60908c3af57e1ea567729cc71416c089737fe2a73e9e79e7a60f00f66c91e4b45ad0d37cd4be2d43fec44963ef14368
- languageName: node
- linkType: hard
-
"resolve@npm:^1.1.6, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.9.0":
version: 1.22.10
resolution: "resolve@npm:1.22.10"
@@ -11384,6 +11358,13 @@ __metadata:
languageName: node
linkType: hard
+"set-cookie-parser@npm:^2.6.0":
+ version: 2.7.1
+ resolution: "set-cookie-parser@npm:2.7.1"
+ checksum: 2ef8b351094712f8f7df6d63ed4b10350b24a5b515772690e7dec227df85fcef5bc451c7765f484fd9f36694ece5438d1456407d017f237d0d3351d7dbbd3587
+ languageName: node
+ linkType: hard
+
"set-function-length@npm:^1.2.2":
version: 1.2.2
resolution: "set-function-length@npm:1.2.2"
@@ -12364,14 +12345,7 @@ __metadata:
languageName: node
linkType: hard
-"tiny-invariant@npm:^1.0.2":
- version: 1.3.3
- resolution: "tiny-invariant@npm:1.3.3"
- checksum: 5e185c8cc2266967984ce3b352a4e57cb89dad5a8abb0dea21468a6ecaa67cd5bb47a3b7a85d08041008644af4f667fb8b6575ba38ba5fb00b3b5068306e59fe
- languageName: node
- linkType: hard
-
-"tiny-warning@npm:^1.0.0, tiny-warning@npm:^1.0.2":
+"tiny-warning@npm:^1.0.2":
version: 1.0.3
resolution: "tiny-warning@npm:1.0.3"
checksum: da62c4acac565902f0624b123eed6dd3509bc9a8d30c06e017104bedcf5d35810da8ff72864400ad19c5c7806fc0a8323c68baf3e326af7cb7d969f846100d71
@@ -12962,13 +12936,6 @@ __metadata:
languageName: node
linkType: hard
-"value-equal@npm:^1.0.1":
- version: 1.0.1
- resolution: "value-equal@npm:1.0.1"
- checksum: bb7ae1facc76b5cf8071aeb6c13d284d023fdb370478d10a5d64508e0e6e53bb459c4bbe34258df29d82e6f561f874f0105eba38de0e61fe9edd0bdce07a77a2
- languageName: node
- linkType: hard
-
"vary@npm:~1.1.2":
version: 1.1.2
resolution: "vary@npm:1.1.2"
@@ -13014,7 +12981,7 @@ __metadata:
languageName: node
linkType: hard
-"warning@npm:^4.0.1, warning@npm:^4.0.2, warning@npm:^4.0.3":
+"warning@npm:^4.0.2, warning@npm:^4.0.3":
version: 4.0.3
resolution: "warning@npm:4.0.3"
dependencies: