'; export const STATEFUL_BUTTON_STATES = { pending: 'pending', @@ -17,3 +17,9 @@ export const BADGE_STATES = { danger: 'danger', secondary: 'secondary', }; + +export const NOTIFICATION_MESSAGES = { + saving: 'Saving', + duplicating: 'Duplicating', + deleting: 'Deleting', +}; diff --git a/src/course-updates/CourseUpdates.jsx b/src/course-updates/CourseUpdates.jsx new file mode 100644 index 000000000..cfcdfea96 --- /dev/null +++ b/src/course-updates/CourseUpdates.jsx @@ -0,0 +1,163 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { + Button, + Container, + Layout, +} from '@edx/paragon'; +import { Add as AddIcon } from '@edx/paragon/icons'; +import { useSelector } from 'react-redux'; + +import { getProcessingNotification } from '../generic/processing-notification/data/selectors'; +import ProcessingNotification from '../generic/processing-notification'; +import SubHeader from '../generic/sub-header/SubHeader'; +import InternetConnectionAlert from '../generic/internet-connection-alert'; +import { RequestStatus } from '../data/constants'; +import CourseHandouts from './course-handouts/CourseHandouts'; +import CourseUpdate from './course-update/CourseUpdate'; +import DeleteModal from './delete-modal/DeleteModal'; +import UpdateForm from './update-form/UpdateForm'; +import { REQUEST_TYPES } from './constants'; +import messages from './messages'; +import { useCourseUpdates } from './hooks'; +import { getLoadingStatuses, getSavingStatuses } from './data/selectors'; +import { matchesAnyStatus } from './utils'; + +const CourseUpdates = ({ courseId }) => { + const intl = useIntl(); + + const { + requestType, + courseUpdates, + courseHandouts, + courseUpdatesInitialValues, + isMainFormOpen, + isInnerFormOpen, + isUpdateFormOpen, + isDeleteModalOpen, + closeUpdateForm, + closeDeleteModal, + handleUpdatesSubmit, + handleOpenUpdateForm, + handleOpenDeleteForm, + handleDeleteUpdateSubmit, + } = useCourseUpdates({ courseId }); + + const { + isShow: isShowProcessingNotification, + title: processingNotificationTitle, + } = useSelector(getProcessingNotification); + + const loadingStatuses = useSelector(getLoadingStatuses); + const savingStatuses = useSelector(getSavingStatuses); + + const anyStatusFailed = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.FAILED); + const anyStatusInProgress = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.IN_PROGRESS); + const anyStatusPending = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.PENDING); + + return ( + <> +
Some text
', + date: 'August 29, 2023', + }; + + axiosMock + .onPost(getCourseUpdatesApiUrl(courseId)) + .reply(200, data); + + await executeThunk(createCourseUpdateQuery(courseId, data), store.dispatch); + expect(getByText('Some text')).toBeInTheDocument(); + expect(getByText(data.date)).toBeInTheDocument(); + }); + + it('should edit course update', async () => { + const { getByText, queryByText } = render(Some text
', + date: 'August 29, 2023', + }; + + axiosMock + .onPut(updateCourseUpdatesApiUrl(courseId, courseUpdatesMock[0].id)) + .reply(200, data); + + await executeThunk(editCourseUpdateQuery(courseId, data), store.dispatch); + expect(getByText('Some text')).toBeInTheDocument(); + expect(getByText(data.date)).toBeInTheDocument(); + expect(queryByText(courseUpdatesMock[0].date)).not.toBeInTheDocument(); + expect(queryByText(courseUpdatesMock[0].content)).not.toBeInTheDocument(); + }); + + it('should delete course update', async () => { + const { queryByText } = render(Some handouts 1
', + }; + + axiosMock + .onPut(getCourseHandoutApiUrl(courseId)) + .reply(200, data); + + await executeThunk(editCourseHandoutsQuery(courseId, data), store.dispatch); + expect(getByText('Some handouts 1')).toBeInTheDocument(); + expect(queryByText(courseHandoutsMock.data)).not.toBeInTheDocument(); + }); + + it('Add new update form is visible after clicking "New update" button', async () => { + const { getByText, getByRole, getAllByRole } = render({intl.formatMessage(messages.errorMessage)}
+