Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/tab-page/TabPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const mockUseToast = (overrides = {}) => useToast.mockReturnValue({

describe('Tab Page', () => {
const mockData = {
courseId: 'test-course',
courseStatus: 'loaded',
};

Expand Down
71 changes: 39 additions & 32 deletions src/tab-page/TabPage.jsx → src/tab-page/TabPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import React, { type ReactNode } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux';
import { Navigate } from 'react-router-dom';
Expand All @@ -11,27 +10,41 @@ import PageLoading from '../generic/PageLoading';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import { useModel } from '../generic/model-store';
import { useToast } from '../generic/ToastContext';
import type { RootState } from '../store';
import {
LOADING, LOADED, DENIED, type StatusValue,
} from '../constants';

import genericMessages from '../generic/messages';
import messages from './messages';
import LoadedTabPage from './LoadedTabPage';
import LaunchCourseHomeTourButton from '../product-tours/newUserCourseHomeTour/LaunchCourseHomeTourButton';
import { TourProvider } from '../product-tours/TourContext';

const TabPage = (props) => {
interface TabPageProps {
activeTabSlug: string;
courseId?: string;
courseStatus: StatusValue;
metadataModel: string;
unitId?: string;
children?: ReactNode;
}

const TabPage = ({
activeTabSlug,
courseId,
courseStatus,
metadataModel,
unitId,
children,
}: TabPageProps) => {
const intl = useIntl();
const {
activeTabSlug,
courseId,
courseStatus,
metadataModel,
} = props;
const {
errorMessage: courseHomeErrorMessage,
} = useSelector(state => state.courseHome);
} = useSelector((state: RootState) => state.courseHome);
const {
errorMessage: coursewareErrorMessage,
} = useSelector(state => state.courseware);
} = useSelector((state: RootState) => state.courseware);
const errorMessage = courseHomeErrorMessage || coursewareErrorMessage;
const { toastContent, isToastOpen, closeToast } = useToast();
const {
Expand All @@ -42,7 +55,7 @@ const TabPage = (props) => {
title,
} = useModel('courseHomeMeta', courseId);

if (courseStatus === 'denied') {
if (courseStatus === DENIED) {
const redirectUrl = getAccessDeniedRedirectUrl(courseId, activeTabSlug, courseAccess, start);
if (redirectUrl) {
return (<Navigate to={redirectUrl} replace />);
Expand All @@ -51,32 +64,39 @@ const TabPage = (props) => {

return (
<TourProvider>
{['loaded', 'denied'].includes(courseStatus) && (
{(courseStatus === LOADED || courseStatus === DENIED) && (
<>
<Toast
action={toastContent?.action ?? null}
action={toastContent?.action}
closeLabel={intl.formatMessage(genericMessages.close)}
onClose={closeToast}
show={isToastOpen}
>
{toastContent?.message}
{toastContent?.message ?? ''}
</Toast>
{metadataModel === 'courseHomeMeta' && (<LaunchCourseHomeTourButton srOnly />)}
</>
)}

<HeaderSlot courseOrg={org} courseNumber={number} courseTitle={title} />

{courseStatus === 'loading' && (
{courseStatus === LOADING && (
<PageLoading srMessage={intl.formatMessage(messages.loading)} />
)}

{['loaded', 'denied'].includes(courseStatus) && (
<LoadedTabPage {...props} />
{(courseStatus === LOADED || courseStatus === DENIED) && courseId && (
<LoadedTabPage
activeTabSlug={activeTabSlug}
courseId={courseId}
metadataModel={metadataModel}
unitId={unitId}
>
{children}
</LoadedTabPage>
)}

{/* courseStatus 'failed' and any other unexpected course status. */}
{(!['loading', 'loaded', 'denied'].includes(courseStatus)) && (
{courseStatus !== LOADING && courseStatus !== LOADED && courseStatus !== DENIED && (
<p className="text-center py-5 mx-auto" style={{ maxWidth: '30em' }}>
{errorMessage || intl.formatMessage(messages.failure)}
</p>
Expand All @@ -86,17 +106,4 @@ const TabPage = (props) => {
);
};

TabPage.defaultProps = {
courseId: null,
unitId: null,
};

TabPage.propTypes = {
activeTabSlug: PropTypes.string.isRequired,
courseId: PropTypes.string,
courseStatus: PropTypes.string.isRequired,
metadataModel: PropTypes.string.isRequired,
unitId: PropTypes.string,
};

export default TabPage;