{
isFetchError,
}}
/>
+
+
+
+
+
+ {isVideoEditorModalOpen && (
+
+ )}
);
};
-VideoGallery.propTypes = {};
+VideoGallery.propTypes = {
+ onCancel: PropTypes.func,
+ returnFunction: PropTypes.func,
+};
export default VideoGallery;
diff --git a/src/editors/containers/VideoGallery/index.test.jsx b/src/editors/containers/VideoGallery/index.test.jsx
index 1cffffea8..6e45a02d2 100644
--- a/src/editors/containers/VideoGallery/index.test.jsx
+++ b/src/editors/containers/VideoGallery/index.test.jsx
@@ -6,6 +6,8 @@ import React from 'react';
import {
act, fireEvent, render, screen,
} from '@testing-library/react';
+import * as reactRouterDom from 'react-router-dom';
+import * as reduxThunks from '../../data/redux';
import VideoGallery from './index';
@@ -120,11 +122,10 @@ describe('VideoGallery', () => {
expect(screen.getByText(video.client_video_id)).toBeInTheDocument()
));
});
- it('navigates to video upload page when there are no videos', async () => {
- expect(window.location.replace).not.toHaveBeenCalled();
+ it('renders video upload modal when there are no videos', async () => {
updateState({ videos: [] });
await renderComponent();
- expect(window.location.replace).toHaveBeenCalled();
+ expect(screen.getByRole('heading', { name: /upload or embed a new video/i })).toBeInTheDocument();
});
it.each([
[/newest/i, [2, 1, 3]],
@@ -191,5 +192,36 @@ describe('VideoGallery', () => {
expect(screen.queryByText('client_id_1')).not.toBeInTheDocument();
expect(screen.queryByText('client_id_3')).not.toBeInTheDocument();
});
+
+ it('calls onVideoUpload correctly when a video is uploaded', async () => {
+ // Mock useSearchParams
+ const setSearchParams = jest.fn();
+ jest.spyOn(reactRouterDom, 'useSearchParams').mockReturnValue([{}, setSearchParams]);
+
+ // Mock the uploadVideo thunk to immediately call postUploadRedirect
+ jest.spyOn(reduxThunks.thunkActions.video, 'uploadVideo').mockImplementation(
+ ({ postUploadRedirect }) => () => {
+ if (postUploadRedirect) {
+ postUploadRedirect('http://test.video/url.mp4');
+ }
+ return { type: 'MOCK_UPLOAD_VIDEO' };
+ },
+ );
+
+ await renderComponent();
+
+ // Open the upload modal by clicking the button
+ const openModalButton = screen.getByRole('button', { name: /upload or embed a new video/i });
+ fireEvent.click(openModalButton);
+
+ // Wait for the input to appear in the modal
+ const urlInput = await screen.findByPlaceholderText('Paste your video ID or URL');
+ fireEvent.change(urlInput, { target: { value: 'http://test.video/url.mp4' } });
+
+ const submitButton = screen.getByRole('button', { name: /submit/i });
+ fireEvent.click(submitButton);
+
+ expect(setSearchParams).toHaveBeenCalledWith({ selectedVideoUrl: 'http://test.video/url.mp4' });
+ });
});
});
diff --git a/src/editors/containers/VideoGallery/messages.js b/src/editors/containers/VideoGallery/messages.js
index e26dd63db..3dd446b7c 100644
--- a/src/editors/containers/VideoGallery/messages.js
+++ b/src/editors/containers/VideoGallery/messages.js
@@ -21,7 +21,16 @@ const messages = {
defaultMessage: 'Upload or embed a new video',
description: 'Label for upload button',
},
-
+ videoUploadModalTitle: {
+ id: 'authoring.selectvideomodal.upload.title',
+ defaultMessage: 'Upload or embed a new video',
+ description: 'Label for upload modal',
+ },
+ videoEditorModalTitle: {
+ id: 'authoring.selectvideomodal.edit.title',
+ defaultMessage: 'Edit selected video',
+ description: 'Label for editor modal',
+ },
// Sort Dropdown
sortByDateNewest: {
id: 'authoring.selectvideomodal.sort.datenewest.label',
diff --git a/src/editors/containers/VideoUploadEditor/VideoUploader.jsx b/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
index 028d1c085..09d943db8 100644
--- a/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
+++ b/src/editors/containers/VideoUploadEditor/VideoUploader.jsx
@@ -10,9 +10,9 @@ import { thunkActions } from '../../data/redux';
import * as hooks from './hooks';
import messages from './messages';
-const URLUploader = () => {
+const URLUploader = ({ onUpload }) => {
const [textInputValue, setTextInputValue] = React.useState('');
- const onURLUpload = hooks.onVideoUpload('selectedVideoUrl');
+ const onURLUpload = hooks.onVideoUpload('selectedVideoUrl', onUpload);
const intl = useIntl();
return (
@@ -58,16 +58,16 @@ const URLUploader = () => {
);
};
-export const VideoUploader = ({ setLoading }) => {
+export const VideoUploader = ({ setLoading, onUpload, onClose }) => {
const dispatch = useDispatch();
const intl = useIntl();
- const goBack = hooks.useHistoryGoBack();
+ const goBack = onClose || hooks.useHistoryGoBack();
const handleProcessUpload = ({ fileData }) => {
dispatch(thunkActions.video.uploadVideo({
supportedFiles: [fileData],
setLoadSpinner: setLoading,
- postUploadRedirect: hooks.onVideoUpload('selectedVideoId'),
+ postUploadRedirect: hooks.onVideoUpload('selectedVideoId', onUpload),
}));
};
@@ -85,14 +85,20 @@ export const VideoUploader = ({ setLoading }) => {
}
+ inputComponent={}
/>
);
};
+URLUploader.propTypes = {
+ onUpload: PropTypes.func,
+};
+
VideoUploader.propTypes = {
setLoading: PropTypes.func.isRequired,
+ onUpload: PropTypes.func,
+ onClose: PropTypes.func,
};
export default VideoUploader;
diff --git a/src/editors/containers/VideoUploadEditor/hooks.js b/src/editors/containers/VideoUploadEditor/hooks.js
index a2774d9c6..3cc1f8468 100644
--- a/src/editors/containers/VideoUploadEditor/hooks.js
+++ b/src/editors/containers/VideoUploadEditor/hooks.js
@@ -11,15 +11,20 @@ export const {
navigateTo,
} = appHooks;
-export const postUploadRedirect = (storeState, uploadType = 'selectedVideoUrl') => {
+export const postUploadRedirect = (storeState, uploadType = 'selectedVideoUrl', onUpload = null) => {
const learningContextId = selectors.app.learningContextId(storeState);
const blockId = selectors.app.blockId(storeState);
+ if (onUpload) {
+ return (videoUrl) => {
+ onUpload(videoUrl, learningContextId, blockId);
+ };
+ }
return (videoUrl) => navigateTo(`/course/${learningContextId}/editor/video/${blockId}?${uploadType}=${videoUrl}`);
};
-export const onVideoUpload = (uploadType) => {
+export const onVideoUpload = (uploadType, onUpload) => {
const storeState = store.getState();
- return module.postUploadRedirect(storeState, uploadType);
+ return module.postUploadRedirect(storeState, uploadType, onUpload);
};
export const useUploadVideo = async ({
diff --git a/src/editors/containers/VideoUploadEditor/index.jsx b/src/editors/containers/VideoUploadEditor/index.jsx
index ae2be7b5f..91664d3e0 100644
--- a/src/editors/containers/VideoUploadEditor/index.jsx
+++ b/src/editors/containers/VideoUploadEditor/index.jsx
@@ -1,17 +1,18 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Spinner } from '@openedx/paragon';
import './index.scss';
import messages from './messages';
import { VideoUploader } from './VideoUploader';
-const VideoUploadEditor = () => {
+const VideoUploadEditor = ({ onUpload, onClose }) => {
const [loading, setLoading] = React.useState(false);
const intl = useIntl();
return (!loading) ? (
-
+
) : (
{
);
};
+VideoUploadEditor.propTypes = {
+ onUpload: PropTypes.func,
+ onClose: PropTypes.func,
+};
+
export default VideoUploadEditor;