From ff6d6269eebbf93fd5d0d5fb298bf3c652965498 Mon Sep 17 00:00:00 2001 From: KristinAoki Date: Thu, 7 Nov 2024 14:18:13 -0500 Subject: [PATCH 1/3] feat: add page not found route --- src/generic/PageNotFound.jsx | 43 ++++++++++++++++++++++++++++++++++++ src/generic/messages.ts | 15 +++++++++++++ src/index.jsx | 3 ++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/generic/PageNotFound.jsx diff --git a/src/generic/PageNotFound.jsx b/src/generic/PageNotFound.jsx new file mode 100644 index 0000000000..99ca8fcf45 --- /dev/null +++ b/src/generic/PageNotFound.jsx @@ -0,0 +1,43 @@ +import { getConfig } from '@edx/frontend-platform'; +import { Hyperlink } from '@openedx/paragon'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import FooterSlot from '@openedx/frontend-slot-footer'; + +import HeaderSlot from '../plugin-slots/HeaderSlot'; +import messages from './messages'; + +const PageNotFound = () => { + const { formatMessage } = useIntl(); + + return ( + <> + +
+

+ {formatMessage(messages.pageNotFoundHeader)} +

+

+ {formatMessage( + messages.pageNotFoundBody, + { + homepageLink: ( + + {formatMessage(messages.homepageLink)} + + ), + }, + )} +

+
+ + + ); +}; + +export default PageNotFound; diff --git a/src/generic/messages.ts b/src/generic/messages.ts index 816378ad73..020ab81c67 100644 --- a/src/generic/messages.ts +++ b/src/generic/messages.ts @@ -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; diff --git a/src/index.jsx b/src/index.jsx index ec4f4dc284..af7a153096 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -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'; @@ -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( @@ -46,6 +46,7 @@ subscribe(APP_READY, () => { + } /> } /> } /> } /> From 2a29602f8a1605a2aa73d6c4aada749b877167b6 Mon Sep 17 00:00:00 2001 From: KristinAoki Date: Thu, 7 Nov 2024 16:03:10 -0500 Subject: [PATCH 2/3] feat: add logging --- src/generic/PageNotFound.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/generic/PageNotFound.jsx b/src/generic/PageNotFound.jsx index 99ca8fcf45..44c2c46629 100644 --- a/src/generic/PageNotFound.jsx +++ b/src/generic/PageNotFound.jsx @@ -1,6 +1,8 @@ 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'; @@ -8,6 +10,10 @@ 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 ( <> From bd95d781ee9e6a8d695a57d23bc2940288635fbb Mon Sep 17 00:00:00 2001 From: KristinAoki Date: Fri, 8 Nov 2024 10:15:44 -0500 Subject: [PATCH 3/3] feat: add tests --- src/generic/PageNotFound.test.jsx | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/generic/PageNotFound.test.jsx diff --git a/src/generic/PageNotFound.test.jsx b/src/generic/PageNotFound.test.jsx new file mode 100644 index 0000000000..86aa854f24 --- /dev/null +++ b/src/generic/PageNotFound.test.jsx @@ -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( + + } /> + , + { 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(); + }); +});