Skip to content

Commit 0793239

Browse files
arbrandesclaude
andcommitted
refactor: resolve the instructor dashboard link with frontend-base
The back link substituted the course ID into the route and checked whether the result was a path in this site or an external URL by hand; frontend-base now owns that. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent efa4ff6 commit 0793239

4 files changed

Lines changed: 25 additions & 16 deletions

File tree

‎package-lock.json‎

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"sass": "^1.49.0"
6363
},
6464
"peerDependencies": {
65-
"@openedx/frontend-base": "^2.0.0-alpha.2 || 0.0.0-dev",
65+
"@openedx/frontend-base": "^2.0.0-alpha.15 || 0.0.0-dev",
6666
"@openedx/paragon": "^23",
6767
"@tanstack/react-query": "^5",
6868
"react": "^18",

‎src/components/GradebookHeader/index.jsx‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Link } from 'react-router-dom';
22

3-
import { getUrlByRouteRole, useIntl } from '@openedx/frontend-base';
3+
import { resolveRouteByRole, useIntl } from '@openedx/frontend-base';
44
import { Button } from '@openedx/paragon';
55

66
import { instructorDashboardUrl } from '@src/data/services/lms/urls';
@@ -22,8 +22,7 @@ export const GradebookHeader = () => {
2222
// Prefer the instructor dashboard route if the running site provides one,
2323
// so navigation stays within the SPA; otherwise fall back to a full page
2424
// load of the legacy LMS dashboard.
25-
const dashboardRoute = getUrlByRouteRole(instructorDashboardRole)?.replace(':courseId', courseId);
26-
const isInternalRoute = !!dashboardRoute && !/^[a-z][a-z0-9+.-]*:/i.test(dashboardRoute);
25+
const dashboardRoute = resolveRouteByRole(instructorDashboardRole, { courseId });
2726
const backLinkContent = (
2827
<>
2928
<span aria-hidden="true">{'<< '}</span>
@@ -32,10 +31,10 @@ export const GradebookHeader = () => {
3231
);
3332
return (
3433
<div className="gradebook-header">
35-
{isInternalRoute ? (
36-
<Link to={dashboardRoute} className="mb-3">{backLinkContent}</Link>
34+
{dashboardRoute?.isInternal ? (
35+
<Link to={dashboardRoute.url} className="mb-3">{backLinkContent}</Link>
3736
) : (
38-
<a href={dashboardRoute ?? instructorDashboardUrl()} className="mb-3">{backLinkContent}</a>
37+
<a href={dashboardRoute?.url ?? instructorDashboardUrl()} className="mb-3">{backLinkContent}</a>
3938
)}
4039
<h1>{formatMessage(messages.gradebook)}</h1>
4140
<div className="subtitle-row d-flex justify-content-between align-items-center">

‎src/components/GradebookHeader/index.test.jsx‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { renderWithAllProviders, initializeMocks } from '@src/testUtils';
22
import { screen } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
44

5-
import { getUrlByRouteRole } from '@openedx/frontend-base';
5+
import { resolveRouteByRole } from '@openedx/frontend-base';
66
import { instructorDashboardUrl } from '@src/data/services/lms/urls';
77

88
import { GradebookHeader } from './index';
@@ -11,7 +11,7 @@ import messages from './messages';
1111

1212
jest.mock('@openedx/frontend-base', () => ({
1313
...jest.requireActual('@openedx/frontend-base'),
14-
getUrlByRouteRole: jest.fn(),
14+
resolveRouteByRole: jest.fn(),
1515
}));
1616
jest.mock('@src/data/services/lms/urls', () => ({
1717
instructorDashboardUrl: jest.fn(),
@@ -25,7 +25,7 @@ describe('GradebookHeader', () => {
2525

2626
beforeEach(() => {
2727
jest.clearAllMocks();
28-
getUrlByRouteRole.mockReturnValue(null);
28+
resolveRouteByRole.mockReturnValue(null);
2929
instructorDashboardUrl.mockReturnValue('https://example.com/dashboard');
3030
});
3131

@@ -90,8 +90,15 @@ describe('GradebookHeader', () => {
9090
});
9191

9292
it('renders an SPA link when the site provides an instructor dashboard route', () => {
93-
getUrlByRouteRole.mockReturnValue('/instructor-dashboard/:courseId');
93+
resolveRouteByRole.mockReturnValue({
94+
url: '/instructor-dashboard/course-v1:TestU+CS101+2024',
95+
isInternal: true,
96+
});
9497
renderWithAllProviders(<GradebookHeader />);
98+
expect(resolveRouteByRole).toHaveBeenCalledWith(
99+
'org.openedx.frontend.role.instructorDashboard',
100+
{ courseId: 'course-v1:TestU+CS101+2024' },
101+
);
95102
const dashboardLink = screen.getByRole('link');
96103
expect(dashboardLink).toHaveAttribute(
97104
'href',
@@ -101,7 +108,10 @@ describe('GradebookHeader', () => {
101108
});
102109

103110
it('renders a plain anchor when the instructor dashboard route is external', () => {
104-
getUrlByRouteRole.mockReturnValue('https://other.example.com/dashboard');
111+
resolveRouteByRole.mockReturnValue({
112+
url: 'https://other.example.com/dashboard',
113+
isInternal: false,
114+
});
105115
renderWithAllProviders(<GradebookHeader />);
106116
const dashboardLink = screen.getByRole('link');
107117
expect(dashboardLink).toHaveAttribute(

0 commit comments

Comments
 (0)