diff --git a/package-lock.json b/package-lock.json
index a743a98ee..03e984e81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -47,7 +47,8 @@
"@edx/frontend-platform": "^4.0.0 || ^5.0.0 || ^6.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0",
- "react-dom": "^16.9.0 || ^17.0.0"
+ "react-dom": "^16.9.0 || ^17.0.0",
+ "react-router-dom": "^6.14.2"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
diff --git a/package.json b/package.json
index f0867b37f..f902097c1 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"@edx/frontend-platform": "^4.0.0 || ^5.0.0 || ^6.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0",
- "react-dom": "^16.9.0 || ^17.0.0"
+ "react-dom": "^16.9.0 || ^17.0.0",
+ "react-router-dom": "^6.14.2"
}
}
diff --git a/src/studio-header/CourseLockUp.jsx b/src/studio-header/CourseLockUp.jsx
index d4946c93d..4b3282321 100644
--- a/src/studio-header/CourseLockUp.jsx
+++ b/src/studio-header/CourseLockUp.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { useNavigate, useResolvedPath } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
OverlayTrigger,
@@ -14,26 +15,32 @@ const CourseLockUp = ({
title,
// injected
intl,
-}) => (
-
- {title}
-
+}) => {
+ const navigate = useNavigate();
+ const resolvedPath = useResolvedPath(outlineLink);
+
+ return (
+
+ {title}
+
)}
- >
-
- {org} {number}
- {title}
-
-
-);
+ { e.preventDefault(); navigate(resolvedPath.pathname); }}
+ aria-label={intl.formatMessage(messages['header.label.courseOutline'])}
+ data-testid="course-lock-up-block"
+ >
+ {org} {number}
+ {title}
+
+
+ );
+};
CourseLockUp.propTypes = {
number: PropTypes.string,
diff --git a/src/studio-header/MobileMenu.jsx b/src/studio-header/MobileMenu.jsx
index 3ff31e6c1..2bfd899a0 100644
--- a/src/studio-header/MobileMenu.jsx
+++ b/src/studio-header/MobileMenu.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { NavLink } from 'react-router-dom';
import { Collapsible } from '@edx/paragon';
const MobileMenu = ({
@@ -21,9 +22,14 @@ const MobileMenu = ({
diff --git a/src/studio-header/NavDropdownMenu.jsx b/src/studio-header/NavDropdownMenu.jsx
index 4cacf3adb..80a56b885 100644
--- a/src/studio-header/NavDropdownMenu.jsx
+++ b/src/studio-header/NavDropdownMenu.jsx
@@ -1,10 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { useNavigate, useResolvedPath } from 'react-router-dom';
+
import {
Dropdown,
DropdownButton,
} from '@edx/paragon';
+const NavDropdownItem = ({ item }) => {
+ const navigate = useNavigate();
+ const resolvedPath = useResolvedPath(item.href);
+
+ return (
+ { e.preventDefault(); navigate(resolvedPath.pathname); }}
+ className="small"
+ >
+ {item.title}
+
+ );
+};
+
+NavDropdownItem.propTypes = {
+ item: PropTypes.shape({
+ href: PropTypes.string,
+ title: PropTypes.string,
+ }).isRequired,
+};
+
const NavDropdownMenu = ({
id,
buttonTitle,
@@ -15,14 +39,16 @@ const NavDropdownMenu = ({
title={buttonTitle}
variant="tertiary"
>
- {items.map(item => (
-
- {item.title}
-
- ))}
+ {items.map(item => (/^(?:\w+:)?\/\//.test(item.href)
+ ? (
+
+ {item.title}
+
+ )
+ : ))}
);
diff --git a/src/studio-header/StudioHeader.test.jsx b/src/studio-header/StudioHeader.test.jsx
index 8ebda05cd..961c98e80 100644
--- a/src/studio-header/StudioHeader.test.jsx
+++ b/src/studio-header/StudioHeader.test.jsx
@@ -5,6 +5,7 @@ import {
fireEvent,
waitFor,
} from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
@@ -40,15 +41,17 @@ const RootWrapper = ({
return (
// eslint-disable-next-line react/jsx-no-constructed-context-values, react/prop-types
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
};
diff --git a/src/studio-header/utils.js b/src/studio-header/utils.js
index c4b36589c..f734c65c7 100644
--- a/src/studio-header/utils.js
+++ b/src/studio-header/utils.js
@@ -8,7 +8,7 @@ const getUserMenuItems = ({
}) => {
let items = [
{
- href: `${studioBaseUrl}}`,
+ href: `${studioBaseUrl}`,
title: intl.formatMessage(messages['header.user.menu.studio']),
}, {
href: `${logoutUrl}`,
@@ -18,7 +18,7 @@ const getUserMenuItems = ({
if (isAdmin) {
items = [
{
- href: `${studioBaseUrl}}`,
+ href: `${studioBaseUrl}`,
title: intl.formatMessage(messages['header.user.menu.studio']),
}, {
href: `${studioBaseUrl}/maintenance`,