Skip to content

Commit

Permalink
feat: Add slots for video and file upload components and alerts
Browse files Browse the repository at this point in the history
This change add plugin slots for the file and video upload components, and the alerts components on those pages.

(cherry picked from commit 9c79a41)
  • Loading branch information
xitij2000 committed Dec 20, 2024
1 parent bb553c5 commit c8f5b3a
Show file tree
Hide file tree
Showing 10 changed files with 541 additions and 500 deletions.
2 changes: 2 additions & 0 deletions src/course-outline/page-alerts/PageAlerts.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import CourseOutlinePageAlertsSlot from 'CourseAuthoring/plugin-slots/CourseOutlinePageAlertsSlot';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
Expand Down Expand Up @@ -413,6 +414,7 @@ const PageAlerts = ({
{errorFilesPasteAlert()}
{conflictingFilesPasteAlert()}
{newFilesPasteAlert()}
<CourseOutlinePageAlertsSlot />
</>
);
};
Expand Down
10 changes: 6 additions & 4 deletions src/course-outline/page-alerts/PageAlerts.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { act, render, fireEvent } from '@testing-library/react';
import { act, render, fireEvent, screen, waitFor } from '@testing-library/react';

Check failure on line 3 in src/course-outline/page-alerts/PageAlerts.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected a line break after this opening brace

Check failure on line 3 in src/course-outline/page-alerts/PageAlerts.test.jsx

View workflow job for this annotation

GitHub Actions / tests

'screen' is defined but never used

Check failure on line 3 in src/course-outline/page-alerts/PageAlerts.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected a line break before this closing brace
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp, getConfig } from '@edx/frontend-platform';
Expand Down Expand Up @@ -103,9 +103,11 @@ describe('<PageAlerts />', () => {
const discussionAlertDismissKey = `discussionAlertDismissed-${pageAlertsData.courseId}`;
expect(localStorage.getItem(discussionAlertDismissKey)).toBe('true');

const feedbackLink = queryByText(messages.discussionNotificationFeedback.defaultMessage);
expect(feedbackLink).toBeInTheDocument();
expect(feedbackLink).toHaveAttribute('href', 'some-feedback-url');
await waitFor(() => {
const feedbackLink = queryByText(messages.discussionNotificationFeedback.defaultMessage);
expect(feedbackLink).toBeInTheDocument();
expect(feedbackLink).toHaveAttribute('href', 'some-feedback-url');
});
});

it('renders deprecation warning alerts', async () => {
Expand Down
192 changes: 18 additions & 174 deletions src/files-and-videos/files-page/FilesPage.jsx
Original file line number Diff line number Diff line change
@@ -1,173 +1,39 @@
import React, { useEffect } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Container } from '@openedx/paragon';
import CourseFilesSlot from 'CourseAuthoring/plugin-slots/CourseFilesSlot';
import PropTypes from 'prop-types';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
import { CheckboxFilter, Container } from '@openedx/paragon';
import Placeholder from '@edx/frontend-lib-content-components';

import Placeholder from '@edx/frontend-lib-content-components';
import { RequestStatus } from '../../data/constants';
import { useModels, useModel } from '../../generic/model-store';
import {
addAssetFile,
deleteAssetFile,
fetchAssets,
updateAssetLock,
fetchAssetDownload,
getUsagePaths,
resetErrors,
updateAssetOrder,
validateAssetFiles,
} from './data/thunks';
import messages from './messages';
import FilesPageProvider from './FilesPageProvider';
import { useModel } from '../../generic/model-store';
import getPageHeadTitle from '../../generic/utils';
import {
AccessColumn,
ActiveColumn,
EditFileErrors,
FileTable,
ThumbnailColumn,
} from '../generic';
import { getFileSizeToClosestByte } from '../../utils';
import FileThumbnail from './FileThumbnail';
import FileInfoModalSidebar from './FileInfoModalSidebar';
import FileValidationModal from './FileValidationModal';
import { EditFileErrors } from '../generic';
import { fetchAssets, resetErrors } from './data/thunks';
import FilesPageProvider from './FilesPageProvider';
import messages from './messages';

const FilesPage = ({
courseId,
// injected
intl,
}) => {
const dispatch = useDispatch();
const intl = useIntl();
const courseDetails = useModel('courseDetails', courseId);
document.title = getPageHeadTitle(courseDetails?.name, intl.formatMessage(messages.heading));

useEffect(() => {
dispatch(fetchAssets(courseId));
}, [courseId]);

const {
assetIds,
loadingStatus,
addingStatus: addAssetStatus,
deletingStatus: deleteAssetStatus,
updatingStatus: updateAssetStatus,
usageStatus: usagePathStatus,
errors: errorMessages,
} = useSelector(state => state.assets);

const handleErrorReset = (error) => dispatch(resetErrors(error));
const handleDeleteFile = (id) => dispatch(deleteAssetFile(courseId, id));
const handleDownloadFile = (selectedRows) => dispatch(fetchAssetDownload({ selectedRows, courseId }));
const handleAddFile = (files) => {
handleErrorReset({ errorType: 'add' });
dispatch(validateAssetFiles(courseId, files));
};
const handleFileOverwrite = (close, files) => {
Object.values(files).forEach(file => dispatch(addAssetFile(courseId, file, true)));
close();
};
const handleLockFile = (fileId, locked) => {
handleErrorReset({ errorType: 'lock' });
dispatch(updateAssetLock({ courseId, assetId: fileId, locked }));
};
const handleUsagePaths = (asset) => dispatch(getUsagePaths({ asset, courseId }));
const handleFileOrder = ({ newFileIdOrder, sortType }) => {
dispatch(updateAssetOrder(courseId, newFileIdOrder, sortType));
};

const thumbnailPreview = (props) => FileThumbnail(props);
const infoModalSidebar = (asset) => FileInfoModalSidebar({
asset,
handleLockedAsset: handleLockFile,
});

const assets = useModels('assets', assetIds);
const data = {
fileIds: assetIds,
loadingStatus,
usagePathStatus,
usageErrorMessages: errorMessages.usageMetrics,
fileType: 'file',
};
const maxFileSize = 20 * 1048576;

const activeColumn = {
id: 'activeStatus',
Header: 'Active',
accessor: 'activeStatus',
Cell: ({ row }) => ActiveColumn({ row, pageLoadStatus: loadingStatus }),
Filter: CheckboxFilter,
filter: 'exactTextCase',
filterChoices: [
{ name: intl.formatMessage(messages.activeCheckboxLabel), value: 'active' },
{ name: intl.formatMessage(messages.inactiveCheckboxLabel), value: 'inactive' },
],
};
const accessColumn = {
id: 'lockStatus',
Header: 'Access',
accessor: 'lockStatus',
Cell: ({ row }) => AccessColumn({ row }),
Filter: CheckboxFilter,
filterChoices: [
{ name: intl.formatMessage(messages.lockedCheckboxLabel), value: 'locked' },
{ name: intl.formatMessage(messages.publicCheckboxLabel), value: 'public' },
],
};
const thumbnailColumn = {
id: 'thumbnail',
Header: '',
Cell: ({ row }) => ThumbnailColumn({ row, thumbnailPreview }),
};
const fileSizeColumn = {
id: 'fileSize',
Header: 'File size',
accessor: 'fileSize',
Cell: ({ row }) => {
const { fileSize } = row.original;
return getFileSizeToClosestByte(fileSize);
},
};
useEffect(() => {
dispatch(fetchAssets(courseId));
}, [courseId]);

const tableColumns = [
{ ...thumbnailColumn },
{
Header: 'File name',
accessor: 'displayName',
},
{ ...fileSizeColumn },
{
Header: 'Type',
accessor: 'wrapperType',
Filter: CheckboxFilter,
filter: 'includesValue',
filterChoices: [
{
name: intl.formatMessage(messages.codeCheckboxLabel),
value: 'code',
},
{
name: intl.formatMessage(messages.imageCheckboxLabel),
value: 'image',
},
{
name: intl.formatMessage(messages.documentCheckboxLabel),
value: 'document',
},
{
name: intl.formatMessage(messages.audioCheckboxLabel),
value: 'audio',
},
{
name: intl.formatMessage(messages.otherCheckboxLabel),
value: 'other',
},
],
},
{ ...activeColumn },
{ ...accessColumn },
];
const handleErrorReset = (error) => dispatch(resetErrors(error));

if (loadingStatus === RequestStatus.DENIED) {
return (
Expand All @@ -189,30 +55,10 @@ const FilesPage = ({
loadingStatus={loadingStatus}
/>
<div className="h2">
<FormattedMessage {...messages.heading} />
{intl.formatMessage(messages.heading)}
</div>
{loadingStatus !== RequestStatus.FAILED && (
<>
<FileTable
{...{
courseId,
data,
handleAddFile,
handleDeleteFile,
handleDownloadFile,
handleLockFile,
handleUsagePaths,
handleErrorReset,
handleFileOrder,
tableColumns,
maxFileSize,
thumbnailPreview,
infoModalSidebar,
files: assets,
}}
/>
<FileValidationModal {...{ handleFileOverwrite }} />
</>
<CourseFilesSlot courseId={courseId} />
)}
</Container>
</FilesPageProvider>
Expand All @@ -221,8 +67,6 @@ const FilesPage = ({

FilesPage.propTypes = {
courseId: PropTypes.string.isRequired,
// injected
intl: intlShape.isRequired,
};

export default injectIntl(FilesPage);
export default FilesPage;
Loading

0 comments on commit c8f5b3a

Please sign in to comment.