Skip to content

Commit

Permalink
feat: add page not found route (#1514)
Browse files Browse the repository at this point in the history
* feat: add page not found route

* feat: add logging

* feat: add tests
  • Loading branch information
KristinAoki authored Nov 12, 2024
1 parent 0effb32 commit 4da37f3
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 1 deletion.
49 changes: 49 additions & 0 deletions src/generic/PageNotFound.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getConfig } from '@edx/frontend-platform';
import { Hyperlink } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { logError } from '@edx/frontend-platform/logging';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import FooterSlot from '@openedx/frontend-slot-footer';

import HeaderSlot from '../plugin-slots/HeaderSlot';
import messages from './messages';

const PageNotFound = () => {
const { formatMessage } = useIntl();
const location = window.location.href;

logError('Page failed to load, probably an invalid URL.', location);
sendTrackEvent('edx.ui.lms.page_not_found', { location });

return (
<>
<HeaderSlot />
<main
id="main-content"
className="main-content d-flex justify-content-center align-items-center flex-column"
style={{
height: '50vh',
}}
>
<h1 className="h3">
{formatMessage(messages.pageNotFoundHeader)}
</h1>
<p>
{formatMessage(
messages.pageNotFoundBody,
{
homepageLink: (
<Hyperlink destination={getConfig().LMS_BASE_URL}>
{formatMessage(messages.homepageLink)}
</Hyperlink>
),
},
)}
</p>
</main>
<FooterSlot />
</>
);
};

export default PageNotFound;
41 changes: 41 additions & 0 deletions src/generic/PageNotFound.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getConfig, history } from '@edx/frontend-platform';
import { Routes, Route } from 'react-router-dom';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';

import {
initializeTestStore,
render,
screen,
} from '../setupTest';
import PageNotFound from './PageNotFound';
import messages from './messages';

jest.mock('@edx/frontend-platform/analytics');

describe('PageNotFound', () => {
beforeEach(async () => {
await initializeTestStore();
const invalidUrl = '/new/course';
history.push(invalidUrl);
render(
<Routes>
<Route path="*" element={<PageNotFound />} />
</Routes>,
{ wrapWithRouter: true },
);
});

it('displays page not found header', () => {
expect(screen.getByText(messages.pageNotFoundHeader.defaultMessage)).toBeVisible();
});

it('displays link back to learner dashboard', () => {
const expected = getConfig().LMS_BASE_URL;
const homepageLink = screen.getByRole('link', { name: messages.homepageLink.defaultMessage });
expect(homepageLink).toHaveAttribute('href', expected);
});

it('calls tracking events', () => {
expect(sendTrackEvent).toHaveBeenCalled();
});
});
15 changes: 15 additions & 0 deletions src/generic/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ const messages = defineMessages({
defaultMessage: 'Sign in',
description: 'Text in a button, prompting the user to log in.',
},
pageNotFoundHeader: {
id: 'learning.pageNotFound.header',
defaultMessage: 'Page not found',
description: 'Text for header notifying them that the page is not found',
},
pageNotFoundBody: {
id: 'learning.pageNotFound.body',
defaultMessage: 'The page you you were looking for was not found. Go back to the {homepageLink}.',
description: 'Text for body, prompting the user to go back to the home page',
},
homepageLink: {
id: 'learning.pageNotFound.body.homepageLink.label',
defaultMessage: 'homepage',
description: 'Text for url, telling them the page they will be navigated to',
},
});

export default messages;
3 changes: 2 additions & 1 deletion src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
getConfig,
} from '@edx/frontend-platform';
import { AppProvider, ErrorPage, PageWrap } from '@edx/frontend-platform/react';
import React from 'react';
import ReactDOM from 'react-dom';
import { Routes, Route } from 'react-router-dom';

Expand Down Expand Up @@ -35,6 +34,7 @@ import CourseAccessErrorPage from './generic/CourseAccessErrorPage';
import DecodePageRoute from './decode-page-route';
import { DECODE_ROUTES, ROUTES } from './constants';
import PreferencesUnsubscribe from './preferences-unsubscribe';
import PageNotFound from './generic/PageNotFound';

subscribe(APP_READY, () => {
ReactDOM.render(
Expand All @@ -46,6 +46,7 @@ subscribe(APP_READY, () => {
<NoticesProvider>
<UserMessagesProvider>
<Routes>
<Route path="*" element={<PageWrap><PageNotFound /></PageWrap>} />
<Route path={ROUTES.UNSUBSCRIBE} element={<PageWrap><GoalUnsubscribe /></PageWrap>} />
<Route path={ROUTES.REDIRECT} element={<PageWrap><CoursewareRedirectLandingPage /></PageWrap>} />
<Route path={ROUTES.PREFERENCES_UNSUBSCRIBE} element={<PageWrap><PreferencesUnsubscribe /></PageWrap>} />
Expand Down

0 comments on commit 4da37f3

Please sign in to comment.