From 689bf21215a5b11c320152e734d6862107c9fd2e Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Wed, 8 Nov 2023 21:22:36 +0530 Subject: [PATCH] refactor: improve course outline page --- src/course-outline/CourseOutline.jsx | 9 ++-- src/course-outline/data/api.js | 5 ++ .../HeaderNavigation.test.jsx | 1 + src/course-outline/hooks.jsx | 1 - .../outline-sidebar/OutlineSidebar.jsx | 4 +- .../outline-sidebar/OutlineSidebar.test.jsx | 47 ++++++++++++------- src/course-outline/status-bar/StatusBar.jsx | 4 +- src/course-outline/status-bar/StatusBar.scss | 2 +- src/generic/sub-header/SubHeader.jsx | 5 +- src/help-urls/__mocks__/helpUrls.js | 35 ++++++++++++++ src/help-urls/__mocks__/index.js | 2 + src/help-urls/data/api.js | 4 +- 12 files changed, 85 insertions(+), 34 deletions(-) create mode 100644 src/help-urls/__mocks__/helpUrls.js create mode 100644 src/help-urls/__mocks__/index.js diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx index 39005d9215..51bf39f269 100644 --- a/src/course-outline/CourseOutline.jsx +++ b/src/course-outline/CourseOutline.jsx @@ -50,7 +50,7 @@ const CourseOutline = ({ courseId }) => { return ( <> - +
{showSuccessAlert ? ( @@ -71,7 +71,6 @@ const CourseOutline = ({ courseId }) => { className="mt-5" title={intl.formatMessage(messages.headingTitle)} subtitle={intl.formatMessage(messages.headingSubtitle)} - withSubHeaderContent={false} headerActions={( { @@ -94,7 +93,7 @@ const CourseOutline = ({ courseId }) => {
diff --git a/src/course-outline/data/api.js b/src/course-outline/data/api.js index dbc68e8cb9..9b613bf81c 100644 --- a/src/course-outline/data/api.js +++ b/src/course-outline/data/api.js @@ -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}`; /** diff --git a/src/course-outline/header-navigations/HeaderNavigation.test.jsx b/src/course-outline/header-navigations/HeaderNavigation.test.jsx index dff210eb87..1b83e09191 100644 --- a/src/course-outline/header-navigations/HeaderNavigation.test.jsx +++ b/src/course-outline/header-navigations/HeaderNavigation.test.jsx @@ -22,6 +22,7 @@ const renderComponent = (props) => render( diff --git a/src/course-outline/hooks.jsx b/src/course-outline/hooks.jsx index ec525d8aa7..ae6a9b38dd 100644 --- a/src/course-outline/hooks.jsx +++ b/src/course-outline/hooks.jsx @@ -54,7 +54,6 @@ const useCourseOutline = ({ courseId }) => { }; const handleEnableHighlightsSubmit = () => { - dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); dispatch(enableCourseHighlightsEmailsQuery(courseId)); closeEnableHighlightsModal(); }; diff --git a/src/course-outline/outline-sidebar/OutlineSidebar.jsx b/src/course-outline/outline-sidebar/OutlineSidebar.jsx index 7756ce006b..0440ad6e52 100644 --- a/src/course-outline/outline-sidebar/OutlineSidebar.jsx +++ b/src/course-outline/outline-sidebar/OutlineSidebar.jsx @@ -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'; @@ -41,7 +41,7 @@ const OutlineSideBar = ({ courseId }) => { {descriptions.map((description) => (

{description}

))} - {Boolean(link) && ( + {Boolean(link) && Boolean(link.href) && ( ', () => { }, }); 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(); + }); }); }); diff --git a/src/course-outline/status-bar/StatusBar.jsx b/src/course-outline/status-bar/StatusBar.jsx index 72564d86a1..e842d8cfc5 100644 --- a/src/course-outline/status-bar/StatusBar.jsx +++ b/src/course-outline/status-bar/StatusBar.jsx @@ -59,7 +59,7 @@ const StatusBar = ({ : intl.formatMessage(messages.pacingTypeInstructorPaced)} -
+
{intl.formatMessage(messages.checklistTitle)}
-
+
{intl.formatMessage(messages.highlightEmailsTitle)}
{highlightsEnabledForMessaging ? ( diff --git a/src/course-outline/status-bar/StatusBar.scss b/src/course-outline/status-bar/StatusBar.scss index e54524a7c4..873abef83b 100644 --- a/src/course-outline/status-bar/StatusBar.scss +++ b/src/course-outline/status-bar/StatusBar.scss @@ -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 { diff --git a/src/generic/sub-header/SubHeader.jsx b/src/generic/sub-header/SubHeader.jsx index e920ac0c06..a0146fcd57 100644 --- a/src/generic/sub-header/SubHeader.jsx +++ b/src/generic/sub-header/SubHeader.jsx @@ -11,7 +11,6 @@ const SubHeader = ({ headerActions, titleActions, hideBorder, - withSubHeaderContent, }) => (
@@ -30,7 +29,7 @@ const SubHeader = ({ )}
- {contentTitle && withSubHeaderContent && ( + {contentTitle && (

{contentTitle}

{description} @@ -49,7 +48,6 @@ SubHeader.defaultProps = { headerActions: null, titleActions: null, hideBorder: false, - withSubHeaderContent: true, }; SubHeader.propTypes = { title: PropTypes.string.isRequired, @@ -63,6 +61,5 @@ SubHeader.propTypes = { headerActions: PropTypes.node, titleActions: PropTypes.node, hideBorder: PropTypes.bool, - withSubHeaderContent: PropTypes.bool, }; export default SubHeader; diff --git a/src/help-urls/__mocks__/helpUrls.js b/src/help-urls/__mocks__/helpUrls.js new file mode 100644 index 0000000000..500c558176 --- /dev/null +++ b/src/help-urls/__mocks__/helpUrls.js @@ -0,0 +1,35 @@ +module.exports = { + default: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/index.html', + home: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/getting_started/CA_get_started_Studio.html', + develop_course: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/index.html', + outline: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_outline.html', + unit: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_units.html', + visibility: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/controlling_content_visibility.html', + updates: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/handouts_updates.html', + pages: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/pages.html', + files: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/course_files.html', + textbooks: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/textbooks.html', + schedule: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/set_up_course/studio_add_course_information/index.html', + grading: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/grading/index.html', + team_course: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/set_up_course/studio_add_course_information/studio_course_staffing.html', + team_library: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_components/libraries.html#give-other-users-access-to-your-library', + advanced: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/index.html', + checklist: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/set_up_course/index.html', + import_library: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_components/libraries.html#import-a-library', + import_course: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/releasing_course/export_import_course.html#import-a-course', + export_library: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_components/libraries.html#export-a-library', + export_course: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/releasing_course/export_import_course.html#export-a-course', + welcome: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/getting_started/index.html', + login: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/getting_started/index.html', + register: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/getting_started/index.html', + content_libraries: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_components/libraries.html', + content_groups: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_features/cohorts/cohorted_courseware.html', + enrollment_tracks: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_features/diff_content/enroll_track_courseware.html', + group_configurations: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_features/content_experiments/content_experiments_configure.html#set-up-group-configurations-in-edx-studio', + container: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components', + video: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/video/index.html', + certificates: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/set_up_course/studio_add_course_information/studio_creating_certificates.html', + content_highlights: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_sections.html#set-section-highlights-for-weekly-course-highlight-messages', + image_accessibility: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/accessibility/best_practices_course_content_dev.html#use-best-practices-for-describing-images', + social_sharing: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/social_sharing.html', +}; diff --git a/src/help-urls/__mocks__/index.js b/src/help-urls/__mocks__/index.js new file mode 100644 index 0000000000..5acae05196 --- /dev/null +++ b/src/help-urls/__mocks__/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as helpUrls } from './helpUrls'; diff --git a/src/help-urls/data/api.js b/src/help-urls/data/api.js index 3243f5be33..b612aa419f 100644 --- a/src/help-urls/data/api.js +++ b/src/help-urls/data/api.js @@ -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); }