Skip to content

Commit

Permalink
feat: Course outline - Content empty (#72)
Browse files Browse the repository at this point in the history
* feat: [2u-324] add component

* feat: [2u-324] add translates

* feat: [2u-324] update tests

* feat: [2u-324] update branch

* fix: [2u-324] fixed empty handler
  • Loading branch information
vladislavkeblysh authored and navinkarkera committed Nov 22, 2023
1 parent ed5da64 commit 5219c3c
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 18 deletions.
6 changes: 5 additions & 1 deletion src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useCourseOutline } from './hooks';
import StatusBar from './status-bar/StatusBar';
import EnableHighlightsModal from './enable-highlights-modal/EnableHighlightsModal';
import SectionCard from './section-card/SectionCard';
import EmptyPlaceholder from './empty-placeholder/EmptyPlaceholder';
import messages from './messages';

const CourseOutline = ({ courseId }) => {
Expand Down Expand Up @@ -100,9 +101,12 @@ const CourseOutline = ({ courseId }) => {
openEnableHighlightsModal={openEnableHighlightsModal}
/>
<div className="pt-4">
{/* TODO add create new section handler in EmptyPlaceholder */}
{sectionsList.length ? sectionsList.map((section) => (
<SectionCard section={section} />
)) : null}
)) : (
<EmptyPlaceholder onCreateNewSection={() => ({})} />
)}
</div>
</section>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/course-outline/CourseOutline.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
@import "./status-bar/StatusBar";
@import "./section-card/SectionCard";
@import "./card-header/CardHeader";
@import "./empty-placeholder/EmptyPlaceholder";
@import "./highlights-modal/HighlightsModal";
13 changes: 13 additions & 0 deletions src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import initializeStore from '../store';
import {
courseOutlineIndexMock,
courseOutlineIndexWithoutSections,
courseBestPracticesMock,
courseLaunchMock,
} from './__mocks__';
Expand Down Expand Up @@ -150,4 +151,16 @@ describe('<CourseOutline />', () => {
await executeThunk(enableCourseHighlightsEmailsQuery(courseId), store.dispatch);
expect(getByText('Enabled')).toBeInTheDocument();
});

it('render CourseOutline component without sections correctly', async () => {
axiosMock
.onGet(getCourseOutlineIndexApiUrl(courseId))
.reply(200, courseOutlineIndexWithoutSections);

const { getByTestId } = render(<RootWrapper />);

await waitFor(() => {
expect(getByTestId('empty-placeholder')).toBeInTheDocument();
});
});
});
34 changes: 17 additions & 17 deletions src/course-outline/__mocks__/courseOutlineIndexWithoutSections.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
module.exports = {
courseReleaseDate: 'Set Date',
courseStructure: {},
deprecatedBlocksInfo: {
deprecatedEnabledBlockTypes: [],
course_release_date: 'Set Date',
course_structure: {},
deprecated_blocks_info: {
deprecated_enabled_block_types: [],
blocks: [],
advanceSettingsUrl: '/settings/advanced/course-v1:edx+101+y76',
advance_settings_url: '/settings/advanced/course-v1:edx+101+y76',
},
discussionsIncontextFeedbackUrl: '',
discussionsIncontextLearnmoreUrl: '',
initialState: {
expandedLocators: [
discussions_incontext_feedback_url: '',
discussions_incontext_learnmore_url: '',
initial_state: {
expanded_locators: [
'block-v1:edx+101+y76+type@chapter+block@03de0adc9d1c4cc097062d80eb04abf6',
'block-v1:edx+101+y76+type@sequential+block@8a85e287e30a47e98d8c1f37f74a6a9d',
],
locatorToShow: 'block-v1:edx+101+y76+type@chapter+block@03de0adc9d1c4cc097062d80eb04abf6',
locator_to_show: 'block-v1:edx+101+y76+type@chapter+block@03de0adc9d1c4cc097062d80eb04abf6',
},
languageCode: 'en',
lmsLink: '//localhost:18000/courses/course-v1:edx+101+y76/jump_to/block-v1:edx+101+y76+type@course+block@course',
mfeProctoredExamSettingsUrl: '',
notificationDismissUrl: '/course_notifications/course-v1:edx+101+y76/2',
proctoringErrors: [],
reindexLink: '/course/course-v1:edx+101+y76/search_reindex',
rerunNotificationId: 2,
language_code: 'en',
lms_link: '//localhost:18000/courses/course-v1:edx+101+y76/jump_to/block-v1:edx+101+y76+type@course+block@course',
mfe_proctored_exam_settings_url: '',
notification_dismiss_url: '/course_notifications/course-v1:edx+101+y76/2',
proctoring_errors: [],
reindex_link: '/course/course-v1:edx+101+y76/search_reindex',
rerun_notification_id: 2,
};
31 changes: 31 additions & 0 deletions src/course-outline/empty-placeholder/EmptyContent.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';

import EmptyPlaceholder from './EmptyPlaceholder';
import messages from './messages';

const onCreateNewSectionMock = jest.fn();

const renderComponent = () => render(
<IntlProvider locale="en">
<EmptyPlaceholder onCreateNewSection={onCreateNewSectionMock} />
</IntlProvider>,
);

describe('<EmptyPlaceholder />', () => {
it('renders EmptyPlaceholder component correctly', () => {
const { getByText, getByRole } = renderComponent();

expect(getByText(messages.title.defaultMessage)).toBeInTheDocument();
expect(getByRole('button', { name: messages.button.defaultMessage })).toBeInTheDocument();
});

it('calls the onCreateNewSection function when the button is clicked', () => {
const { getByRole } = renderComponent();

const addButton = getByRole('button', { name: messages.button.defaultMessage });
fireEvent.click(addButton);
expect(onCreateNewSectionMock).toHaveBeenCalledTimes(1);
});
});
39 changes: 39 additions & 0 deletions src/course-outline/empty-placeholder/EmptyPlaceholder.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Add as IconAdd } from '@edx/paragon/icons/es5';
import { Button, OverlayTrigger, Tooltip } from '@edx/paragon';

import messages from './messages';

const EmptyPlaceholder = ({ onCreateNewSection }) => {
const intl = useIntl();

return (
<div className="outline-empty-placeholder bg-gray-100" data-testid="empty-placeholder">
<p className="mb-0 text-gray-500">{intl.formatMessage(messages.title)}</p>
<OverlayTrigger
placement="bottom"
overlay={(
<Tooltip id={intl.formatMessage(messages.tooltip)}>
{intl.formatMessage(messages.tooltip)}
</Tooltip>
)}
>
<Button
variant="outline-success"
iconBefore={IconAdd}
onClick={onCreateNewSection}
>
{intl.formatMessage(messages.button)}
</Button>
</OverlayTrigger>
</div>
);
};

EmptyPlaceholder.propTypes = {
onCreateNewSection: PropTypes.func.isRequired,
};

export default EmptyPlaceholder;
10 changes: 10 additions & 0 deletions src/course-outline/empty-placeholder/EmptyPlaceholder.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.outline-empty-placeholder {
display: flex;
align-items: center;
justify-content: center;
gap: 1.25rem;
border: .0625rem solid $gray-200;
border-radius: .375rem;
box-shadow: inset inset 0 1px .125rem 1px $gray-200;
padding: 2.5rem;
}
18 changes: 18 additions & 0 deletions src/course-outline/empty-placeholder/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
title: {
id: 'course-authoring.course-outline.empty-placeholder.title',
defaultMessage: 'You haven\'t added any content to this course yet.',
},
button: {
id: 'course-authoring.course-outline.empty-placeholder.button.new-section',
defaultMessage: 'New section',
},
tooltip: {
id: 'course-authoring.course-outline.empty-placeholder.button.tooltip',
defaultMessage: 'Click to add a new section',
},
});

export default messages;

0 comments on commit 5219c3c

Please sign in to comment.