Skip to content

Commit

Permalink
refactor: improve course outline page
Browse files Browse the repository at this point in the history
  • Loading branch information
navinkarkera committed Nov 22, 2023
1 parent 8f32194 commit 689bf21
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 34 deletions.
9 changes: 4 additions & 5 deletions src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CourseOutline = ({ courseId }) => {

return (
<>
<Container size="xl" className="m-4">
<Container size="xl" className="px-4">
<section className="course-outline-container mb-4 mt-5">
<TransitionReplace>
{showSuccessAlert ? (
Expand All @@ -71,7 +71,6 @@ const CourseOutline = ({ courseId }) => {
className="mt-5"
title={intl.formatMessage(messages.headingTitle)}
subtitle={intl.formatMessage(messages.headingSubtitle)}
withSubHeaderContent={false}
headerActions={(
<HeaderNavigations
isReIndexShow={isReIndexShow}
Expand All @@ -84,8 +83,8 @@ const CourseOutline = ({ courseId }) => {
<Layout
lg={[{ span: 9 }, { span: 3 }]}
md={[{ span: 9 }, { span: 3 }]}
sm={[{ span: 9 }, { span: 3 }]}
xs={[{ span: 9 }, { span: 3 }]}
sm={[{ span: 12 }, { span: 12 }]}
xs={[{ span: 12 }, { span: 12 }]}
xl={[{ span: 9 }, { span: 3 }]}
>
<Layout.Element>
Expand All @@ -94,7 +93,7 @@ const CourseOutline = ({ courseId }) => {
<section className="course-outline-section">
<StatusBar
courseId={courseId}
isLaoding={isLoading}
isLoading={isLoading}
statusBarData={statusBarData}
openEnableHighlightsModal={openEnableHighlightsModal}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/course-outline/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@ import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;

export const getCourseOutlineIndexApiUrl = (courseId) => `${getApiBaseUrl()}/api/contentstore/v1/course_index/${courseId}`;

export const getCourseBestPracticesApiUrl = ({
courseId,
excludeGraded,
all,
}) => `${getApiBaseUrl()}/api/courses/v1/quality/${courseId}/?exclude_graded=${excludeGraded}&all=${all}`;

export const getCourseLaunchApiUrl = ({
courseId,
gradedOnly,
validateOras,
all,
}) => `${getApiBaseUrl()}/api/courses/v1/validation/${courseId}/?graded_only=${gradedOnly}&validate_oras=${validateOras}&all=${all}`;

export const getEnableHighlightsEmailsApiUrl = (courseId) => {
const formattedCourseId = courseId.split('course-v1:')[1];
return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@course+block@course`;
};

export const getCourseReindexApiUrl = (reindexLink) => `${getApiBaseUrl()}${reindexLink}`;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const renderComponent = (props) => render(
<HeaderNavigations
headerNavigationsActions={headerNavigationsActions}
isSectionsExpanded={false}
isDisabledReindexButton={false}
isReIndexShow
{...props}
/>
Expand Down
1 change: 0 additions & 1 deletion src/course-outline/hooks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const useCourseOutline = ({ courseId }) => {
};

const handleEnableHighlightsSubmit = () => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(enableCourseHighlightsEmailsQuery(courseId));
closeEnableHighlightsModal();
};
Expand Down
4 changes: 2 additions & 2 deletions src/course-outline/outline-sidebar/OutlineSidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Hyperlink } from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';

import HelpSidebar from '../../generic/help-sidebar';
import { HelpSidebar } from '../../generic/help-sidebar';
import { useHelpUrls } from '../../help-urls/hooks';
import { getFormattedSidebarMessages } from './utils';

Expand Down Expand Up @@ -41,7 +41,7 @@ const OutlineSideBar = ({ courseId }) => {
{descriptions.map((description) => (
<p className="help-sidebar-about-descriptions" key={description}>{description}</p>
))}
{Boolean(link) && (
{Boolean(link) && Boolean(link.href) && (
<Hyperlink
className="small"
destination={link.href}
Expand Down
47 changes: 29 additions & 18 deletions src/course-outline/outline-sidebar/OutlineSidebar.test.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React from 'react';
import { render } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { initializeMockApp } from '@edx/frontend-platform';
import { AppProvider } from '@edx/frontend-platform/react';

import { helpUrls } from '../../help-urls/__mocks__';
import { getHelpUrlsApiUrl } from '../../help-urls/data/api';
import initializeStore from '../../store';
import OutlineSidebar from './OutlineSidebar';
import messages from './messages';

let axiosMock;
let store;
const mockPathname = '/foo-bar';
const courseId = '123';
Expand Down Expand Up @@ -38,32 +43,38 @@ describe('<OutlineSidebar />', () => {
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock
.onGet(getHelpUrlsApiUrl())
.reply(200, helpUrls);
});

it('render OutlineSidebar component correctly', () => {
it('render OutlineSidebar component correctly', async () => {
const { getByText } = renderComponent();

expect(getByText(messages.section_1_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_1_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_1_descriptions_2.defaultMessage)).toBeInTheDocument();
await waitFor(() => {
expect(getByText(messages.section_1_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_1_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_1_descriptions_2.defaultMessage)).toBeInTheDocument();

expect(getByText(messages.section_2_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_2_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_2_link.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_2_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_2_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_2_link.defaultMessage)).toBeInTheDocument();

expect(getByText(messages.section_3_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_3_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_3_link.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_3_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_3_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_3_link.defaultMessage)).toBeInTheDocument();

expect(getByText(messages.section_4_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_4_descriptions_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_4_title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_4_descriptions_1.defaultMessage)).toBeInTheDocument();

expect(getByText(/To make a section, subsection, or unit unavailable to learners, select the Configure icon for that level, then select the appropriate/i)).toBeInTheDocument();
expect(getByText(/option. Grades for hidden sections, subsections, and units are not included in grade calculations./i)).toBeInTheDocument();
expect(getByText(/To make a section, subsection, or unit unavailable to learners, select the Configure icon for that level, then select the appropriate/i)).toBeInTheDocument();
expect(getByText(/option. Grades for hidden sections, subsections, and units are not included in grade calculations./i)).toBeInTheDocument();

expect(getByText(/To hide the content of a subsection from learners after the subsection due date has passed, select the Configure icon for a subsection, then select/i)).toBeInTheDocument();
expect(getByText(/Grades for the subsection remain included in grade calculations./i)).toBeInTheDocument();
expect(getByText(/To hide the content of a subsection from learners after the subsection due date has passed, select the Configure icon for a subsection, then select/i)).toBeInTheDocument();
expect(getByText(/Grades for the subsection remain included in grade calculations./i)).toBeInTheDocument();

expect(getByText(messages.section_4_link.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.section_4_link.defaultMessage)).toBeInTheDocument();
});
});
});
4 changes: 2 additions & 2 deletions src/course-outline/status-bar/StatusBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const StatusBar = ({
: intl.formatMessage(messages.pacingTypeInstructorPaced)}
</span>
</div>
<div className="outline-status-bar__item">
<div className="outline-status-bar__item mr-4">
<h5 className="h5">{intl.formatMessage(messages.checklistTitle)}</h5>
<Hyperlink
className="small"
Expand All @@ -69,7 +69,7 @@ const StatusBar = ({
{checkListTitle} {intl.formatMessage(messages.checklistCompleted)}
</Hyperlink>
</div>
<div className="outline-status-bar__item">
<div className="outline-status-bar__item ml-4">
<h5 className="h5">{intl.formatMessage(messages.highlightEmailsTitle)}</h5>
<div className="d-flex align-items-end">
{highlightsEnabledForMessaging ? (
Expand Down
2 changes: 1 addition & 1 deletion src/course-outline/status-bar/StatusBar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.outline-status-bar__item {
display: flex;
flex-direction: column;
justify-content: space-between;
justify-content: space-evenly;
min-height: 3.75rem;

& h5 {
Expand Down
5 changes: 1 addition & 4 deletions src/generic/sub-header/SubHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const SubHeader = ({
headerActions,
titleActions,
hideBorder,
withSubHeaderContent,
}) => (
<div className={`${!hideBorder && 'border-bottom border-light-400'} mb-3`}>
<header className="sub-header">
Expand All @@ -30,7 +29,7 @@ const SubHeader = ({
</ActionRow>
)}
</header>
{contentTitle && withSubHeaderContent && (
{contentTitle && (
<header className="sub-header-content">
<h2 className="sub-header-content-title">{contentTitle}</h2>
<span className="small text-gray-700">{description}</span>
Expand All @@ -49,7 +48,6 @@ SubHeader.defaultProps = {
headerActions: null,
titleActions: null,
hideBorder: false,
withSubHeaderContent: true,
};
SubHeader.propTypes = {
title: PropTypes.string.isRequired,
Expand All @@ -63,6 +61,5 @@ SubHeader.propTypes = {
headerActions: PropTypes.node,
titleActions: PropTypes.node,
hideBorder: PropTypes.bool,
withSubHeaderContent: PropTypes.bool,
};
export default SubHeader;
35 changes: 35 additions & 0 deletions src/help-urls/__mocks__/helpUrls.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/help-urls/__mocks__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export { default as helpUrls } from './helpUrls';
4 changes: 3 additions & 1 deletion src/help-urls/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

export const getHelpUrlsApiUrl = () => `${getConfig().STUDIO_BASE_URL}/api/contentstore/v1/help_urls`;

export async function getHelpUrls() {
const { data } = await getAuthenticatedHttpClient()
.get(`${getConfig().STUDIO_BASE_URL}/api/contentstore/v1/help_urls`);
.get(getHelpUrlsApiUrl());
return camelCaseObject(data);
}

0 comments on commit 689bf21

Please sign in to comment.