Skip to content

Commit

Permalink
feat: [FC-0044] Unit page header section (openedx#808)
Browse files Browse the repository at this point in the history
* feat: create Unit page and add page header functionality

* fix: after code review

---------

Co-authored-by: monteri <lansevermore>
  • Loading branch information
ihor-romaniuk authored Feb 5, 2024
1 parent 056a15b commit 9c52b8b
Show file tree
Hide file tree
Showing 55 changed files with 2,347 additions and 60 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ EXAMS_BASE_URL=''
FAVICON_URL=''
LANGUAGE_PREFERENCE_COOKIE_NAME=''
LMS_BASE_URL=''
PREVIEW_BASE_URL=''
LEARNING_BASE_URL=''
LOGIN_URL=''
LOGO_TRADEMARK_URL=''
Expand Down
2 changes: 1 addition & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ ENABLE_PROGRESS_GRAPH_SETTINGS=false
ENABLE_TEAM_TYPE_SETTING=false
ENABLE_NEW_EDITOR_PAGES=true
ENABLE_NEW_VIDEO_UPLOAD_PAGE = false
ENABLE_UNIT_PAGE = false
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN = false
ENABLE_TAGGING_TAXONOMY_PAGES = true
BBB_LEARN_MORE_URL=''
Expand All @@ -43,3 +42,4 @@ HOTJAR_VERSION=6
HOTJAR_DEBUG=true
INVITE_STUDENTS_EMAIL_TO="[email protected]"
AI_TRANSLATIONS_BASE_URL='http://localhost:18760'
PREVIEW_BASE_URL='http://preview.localhost:18000'
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ EXAMS_BASE_URL=
FAVICON_URL='https://edx-cdn.org/v3/default/favicon.ico'
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
PREVIEW_BASE_URL='http://preview.localhost:18000'
LEARNING_BASE_URL='http://localhost:2000'
LOGIN_URL='http://localhost:18000/login'
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
Expand Down
4 changes: 2 additions & 2 deletions src/CourseAuthoringRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Navigate, Routes, Route, useParams,
} from 'react-router-dom';
import { PageWrap } from '@edx/frontend-platform/react';
import Placeholder from '@edx/frontend-lib-content-components';
import CourseAuthoringPage from './CourseAuthoringPage';
import { PagesAndResources } from './pages-and-resources';
import EditorContainer from './editors/EditorContainer';
Expand All @@ -16,6 +15,7 @@ import ScheduleAndDetails from './schedule-and-details';
import { GradingSettings } from './grading-settings';
import CourseTeam from './course-team/CourseTeam';
import { CourseUpdates } from './course-updates';
import { CourseUnit } from './course-unit';
import CourseExportPage from './export-page/CourseExportPage';
import CourseImportPage from './import-page/CourseImportPage';

Expand Down Expand Up @@ -71,7 +71,7 @@ const CourseAuthoringRoutes = () => {
/>
<Route
path="/container/:blockId"
element={process.env.ENABLE_UNIT_PAGE === 'true' ? <PageWrap><Placeholder /></PageWrap> : null}
element={<PageWrap><CourseUnit courseId={courseId} /></PageWrap>}
/>
<Route
path="editor/course-videos/:blockId"
Expand Down
4 changes: 1 addition & 3 deletions src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
React, useState, useEffect,
} from 'react';
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Expand Down
1 change: 0 additions & 1 deletion src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import {
act, render, waitFor, cleanup, fireEvent, within,
} from '@testing-library/react';
Expand Down
25 changes: 23 additions & 2 deletions src/course-outline/card-header/CardHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useSearchParams } from 'react-router-dom';
import {
Dropdown,
Form,
Expand All @@ -14,11 +15,13 @@ import {

import { useEscapeClick } from '../../hooks';
import { ITEM_BADGE_STATUS } from '../constants';
import { scrollToElement } from '../utils';
import messages from './messages';

const CardHeader = ({
title,
status,
cardId,
hasChanges,
onClickPublish,
onClickConfigure,
Expand All @@ -37,11 +40,24 @@ const CardHeader = ({
actions,
}) => {
const intl = useIntl();
const [searchParams] = useSearchParams();
const [titleValue, setTitleValue] = useState(title);
const cardHeaderRef = useRef(null);

const isDisabledPublish = (status === ITEM_BADGE_STATUS.live
|| status === ITEM_BADGE_STATUS.publishedNotLive) && !hasChanges;

useEffect(() => {
const locatorId = searchParams.get('show');
if (!locatorId) {
return;
}

if (cardHeaderRef.current && locatorId === cardId) {
scrollToElement(cardHeaderRef.current);
}
}, []);

useEscapeClick({
onEscape: () => {
setTitleValue(title);
Expand All @@ -51,7 +67,11 @@ const CardHeader = ({
});

return (
<div className="item-card-header" data-testid={`${namePrefix}-card-header`}>
<div
className="item-card-header"
data-testid={`${namePrefix}-card-header`}
ref={cardHeaderRef}
>
{isFormOpen ? (
<Form.Group className="m-0">
<Form.Control
Expand Down Expand Up @@ -151,6 +171,7 @@ const CardHeader = ({
CardHeader.propTypes = {
title: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
cardId: PropTypes.string.isRequired,
hasChanges: PropTypes.bool.isRequired,
onClickPublish: PropTypes.func.isRequired,
onClickConfigure: PropTypes.func.isRequired,
Expand Down
23 changes: 16 additions & 7 deletions src/course-outline/card-header/CardHeader.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';

Expand All @@ -14,11 +14,15 @@ const onClickPublishMock = jest.fn();
const onClickEditMock = jest.fn();
const onClickDeleteMock = jest.fn();
const onClickDuplicateMock = jest.fn();
const onClickConfigureMock = jest.fn();
const onClickMoveUpMock = jest.fn();
const onClickMoveDownMock = jest.fn();
const closeFormMock = jest.fn();

const cardHeaderProps = {
title: 'Some title',
status: ITEM_BADGE_STATUS.live,
cardId: '12345',
hasChanges: false,
onClickMenuButton: onClickMenuButtonMock,
onClickPublish: onClickPublishMock,
Expand All @@ -29,6 +33,9 @@ const cardHeaderProps = {
isDisabledEditField: false,
onClickDelete: onClickDeleteMock,
onClickDuplicate: onClickDuplicateMock,
onClickConfigure: onClickConfigureMock,
onClickMoveUp: onClickMoveUpMock,
onClickMoveDown: onClickMoveDownMock,
namePrefix: 'section',
actions: {
draggable: true,
Expand All @@ -38,7 +45,7 @@ const cardHeaderProps = {
},
};

const renderComponent = (props) => {
const renderComponent = (props, entry = '/') => {
const titleComponent = (
<TitleButton
isExpanded
Expand All @@ -57,11 +64,13 @@ const renderComponent = (props) => {

return render(
<IntlProvider locale="en">
<CardHeader
{...cardHeaderProps}
titleComponent={titleComponent}
{...props}
/>
<MemoryRouter initialEntries={[entry]}>
<CardHeader
{...cardHeaderProps}
titleComponent={titleComponent}
{...props}
/>
</MemoryRouter>,
</IntlProvider>,
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/course-outline/hooks.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useToggle } from '@edx/paragon';
import { useNavigate } from 'react-router-dom';
import { useToggle } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform';

import { RequestStatus } from '../data/constants';
Expand Down
2 changes: 1 addition & 1 deletion src/course-outline/section-card/SectionCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const SectionCard = ({
<div>
{isHeaderVisible && (
<CardHeader
sectionId={id}
cardId={id}
title={displayName}
status={sectionStatus}
hasChanges={hasChanges}
Expand Down
8 changes: 6 additions & 2 deletions src/course-outline/subsection-card/SubsectionCard.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import { useSearchParams } from 'react-router-dom';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button, useToggle } from '@edx/paragon';
import { Add as IconAdd } from '@edx/paragon/icons';
Expand Down Expand Up @@ -33,6 +34,9 @@ const SubsectionCard = ({
const currentRef = useRef(null);
const intl = useIntl();
const dispatch = useDispatch();
const [searchParams] = useSearchParams();
const locatorId = searchParams.get('show');
const isScrolledToElement = locatorId === subsection.id;
const [isFormOpen, openForm, closeForm] = useToggle(false);
const namePrefix = 'subsection';

Expand All @@ -52,7 +56,7 @@ const SubsectionCard = ({
actions.allowMoveUp = canMoveItem(index, -1);
actions.allowMoveDown = canMoveItem(index, 1);

const [isExpanded, setIsExpanded] = useState(!isHeaderVisible);
const [isExpanded, setIsExpanded] = useState(locatorId ? isScrolledToElement : !isHeaderVisible);
const subsectionStatus = getItemStatus({
published,
visibilityState,
Expand Down Expand Up @@ -107,7 +111,7 @@ const SubsectionCard = ({
// if this items has been newly added, scroll to it.
// we need to check section.shouldScroll as whole section is fetched when a
// subsection is duplicated under it.
if (currentRef.current && (section.shouldScroll || subsection.shouldScroll)) {
if (currentRef.current && (section.shouldScroll || subsection.shouldScroll || isScrolledToElement)) {
scrollToElement(currentRef.current);
}
}, []);
Expand Down
64 changes: 42 additions & 22 deletions src/course-outline/subsection-card/SubsectionCard.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import {
act, render, fireEvent, within,
} from '@testing-library/react';
Expand All @@ -15,6 +16,14 @@ import cardHeaderMessages from '../card-header/messages';
// eslint-disable-next-line no-unused-vars
let axiosMock;
let store;
const mockPathname = '/foo-bar';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: mockPathname,
}),
}));

const section = {
id: '123',
Expand Down Expand Up @@ -42,28 +51,30 @@ const subsection = {

const onEditSubectionSubmit = jest.fn();

const renderComponent = (props) => render(
<AppProvider store={store}>
<IntlProvider locale="en">
<SubsectionCard
section={section}
subsection={subsection}
index="1"
canMoveItem={jest.fn()}
onOrderChange={jest.fn()}
onOpenPublishModal={jest.fn()}
onOpenHighlightsModal={jest.fn()}
onOpenDeleteModal={jest.fn()}
onEditClick={jest.fn()}
savingStatus=""
onEditSubmit={onEditSubectionSubmit}
onDuplicateSubmit={jest.fn()}
namePrefix="subsection"
{...props}
>
<span>children</span>
</SubsectionCard>
</IntlProvider>,
const renderComponent = (props, entry = '/') => render(
<AppProvider store={store} wrapWithRouter={false}>
<MemoryRouter initialEntries={[entry]}>
<IntlProvider locale="en">
<SubsectionCard
section={section}
subsection={subsection}
index="1"
canMoveItem={jest.fn()}
onOrderChange={jest.fn()}
onOpenPublishModal={jest.fn()}
onOpenHighlightsModal={jest.fn()}
onOpenDeleteModal={jest.fn()}
onEditClick={jest.fn()}
savingStatus=""
onEditSubmit={onEditSubectionSubmit}
onDuplicateSubmit={jest.fn()}
namePrefix="subsection"
{...props}
>
<span>children</span>
</SubsectionCard>
</IntlProvider>,
</MemoryRouter>,
</AppProvider>,
);

Expand Down Expand Up @@ -197,4 +208,13 @@ describe('<SubsectionCard />', () => {
});
expect(await findByText(cardHeaderMessages.statusBadgeDraft.defaultMessage)).toBeInTheDocument();
});

it('check extended section when URL has a "show" param', async () => {
const { findByTestId } = renderComponent(null, `?show=${section.id}`);

const cardUnits = await findByTestId('subsection-card__units');
const newUnitButton = await findByTestId('new-unit-button');
expect(cardUnits).toBeInTheDocument();
expect(newUnitButton).toBeInTheDocument();
});
});
Loading

0 comments on commit 9c52b8b

Please sign in to comment.