({
+export const state = {
+ showVideoIdChangeAlert: (args) => React.useState(args),
+};
+
+export const sourceHooks = ({ dispatch, previousVideoId, setAlert }) => ({
updateVideoURL: (e, videoId) => {
const videoUrl = e.target.value;
dispatch(actions.video.updateField({ videoSource: videoUrl }));
@@ -22,7 +27,13 @@ export const sourceHooks = ({ dispatch }) => ({
}));
}
},
- updateVideoId: (e) => dispatch(actions.video.updateField({ videoId: e.target.value })),
+ updateVideoId: (e) => {
+ const updatedVideoId = e.target.value;
+ if (previousVideoId !== updatedVideoId && updatedVideoId) {
+ setAlert();
+ }
+ dispatch(actions.video.updateField({ videoId: updatedVideoId }));
+ },
});
export const fallbackHooks = ({ fallbackVideos, dispatch }) => ({
@@ -33,7 +44,19 @@ export const fallbackHooks = ({ fallbackVideos, dispatch }) => ({
},
});
+export const videoIdChangeAlert = () => {
+ const [showVideoIdChangeAlert, setShowVideoIdChangeAlert] = state.showVideoIdChangeAlert(false);
+ return {
+ videoIdChangeAlert: {
+ show: showVideoIdChangeAlert,
+ set: () => setShowVideoIdChangeAlert(true),
+ dismiss: () => setShowVideoIdChangeAlert(false),
+ },
+ };
+};
+
export default {
+ videoIdChangeAlert,
sourceHooks,
fallbackHooks,
};
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx
index 9315a1caa..7bc8d6ad7 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx
@@ -1,8 +1,16 @@
import { dispatch } from 'react-redux';
import { actions } from '../../../../../../data/redux';
+import { MockUseState } from '../../../../../../../testUtils';
import * as requests from '../../../../../../data/redux/thunkActions/requests';
import * as hooks from './hooks';
+jest.mock('react', () => ({
+ ...jest.requireActual('react'),
+ useRef: jest.fn(val => ({ current: val })),
+ useEffect: jest.fn(),
+ useCallback: (cb, prereqs) => ({ cb, prereqs }),
+}));
+
jest.mock('react-redux', () => {
const dispatchFn = jest.fn();
return {
@@ -25,16 +33,24 @@ jest.mock('../../../../../../data/redux/thunkActions/requests', () => ({
checkTranscriptsForImport: jest.fn(),
}));
+const state = new MockUseState(hooks);
+
const youtubeId = 'yOuTuBEiD';
const youtubeUrl = `https://youtu.be/${youtubeId}`;
describe('VideoEditorHandout hooks', () => {
let hook;
-
+ describe('state hooks', () => {
+ state.testGetter(state.keys.showVideoIdChangeAlert);
+ });
describe('sourceHooks', () => {
const e = { target: { value: 'soMEvALuE' } };
beforeEach(() => {
- hook = hooks.sourceHooks({ dispatch });
+ hook = hooks.sourceHooks({
+ dispatch,
+ previousVideoId: 'soMEvALuE',
+ setAlert: jest.fn(),
+ });
});
afterEach(() => {
jest.clearAllMocks();
@@ -82,7 +98,23 @@ describe('VideoEditorHandout hooks', () => {
});
describe('updateVideoId', () => {
it('dispatches updateField action with new videoId', () => {
- hook.updateVideoId(e);
+ hook.updateVideoId({ target: { value: 'newVideoId' } });
+ expect(dispatch).toHaveBeenCalledWith(
+ actions.video.updateField({
+ videoId: e.target.value,
+ }),
+ );
+ });
+ it('dispatches updateField action with empty string', () => {
+ hook.updateVideoId({ target: { value: '' } });
+ expect(dispatch).toHaveBeenCalledWith(
+ actions.video.updateField({
+ videoId: e.target.value,
+ }),
+ );
+ });
+ it('dispatches updateField action with previousVideoId', () => {
+ hook.updateVideoId({ target: { value: 'soMEvALuE' } });
expect(dispatch).toHaveBeenCalledWith(
actions.video.updateField({
videoId: e.target.value,
@@ -120,4 +152,23 @@ describe('VideoEditorHandout hooks', () => {
});
});
});
+ describe('videoIdChangeAlert', () => {
+ beforeEach(() => {
+ state.mock();
+ });
+ afterEach(() => {
+ state.restore();
+ });
+ test('showVideoIdChangeAlert: state values', () => {
+ expect(hooks.videoIdChangeAlert().videoIdChangeAlert.show).toEqual(false);
+ });
+ test('showVideoIdChangeAlert setters: set', () => {
+ hooks.videoIdChangeAlert().videoIdChangeAlert.set();
+ expect(state.setState[state.keys.showVideoIdChangeAlert]).toHaveBeenCalledWith(true);
+ });
+ test('showVideoIdChangeAlert setters: dismiss', () => {
+ hooks.videoIdChangeAlert().videoIdChangeAlert.dismiss();
+ expect(state.setState[state.keys.showVideoIdChangeAlert]).toHaveBeenCalledWith(false);
+ });
+ });
});
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx
index 2337c0adc..b95f63d59 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx
@@ -10,7 +10,6 @@ import {
Button,
Tooltip,
OverlayTrigger,
- FormControlFeedback,
} from '@edx/paragon';
import { DeleteOutline, InfoOutline, Add } from '@edx/paragon/icons';
import {
@@ -24,6 +23,7 @@ import * as hooks from './hooks';
import messages from './messages';
import { selectors } from '../../../../../../data/redux';
+import { ErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert';
import CollapsibleFormWidget from '../CollapsibleFormWidget';
/**
@@ -52,7 +52,12 @@ export const VideoSourceWidget = ({
[widgetHooks.selectorKeys.allowVideoSharing]: widgetHooks.genericWidget,
},
});
- const { updateVideoId, updateVideoURL } = hooks.sourceHooks({ dispatch });
+ const { videoIdChangeAlert } = hooks.videoIdChangeAlert();
+ const { updateVideoId, updateVideoURL } = hooks.sourceHooks({
+ dispatch,
+ previousVideoId: videoId.formValue,
+ setAlert: videoIdChangeAlert.set,
+ });
const {
addFallbackVideo,
deleteFallbackVideo,
@@ -63,6 +68,13 @@ export const VideoSourceWidget = ({
fontSize="x-small"
title={intl.formatMessage(messages.titleLabel)}
>
+
+
+
-
+
-
+
updateVideoURL(e, videoId.local)}
value={source.local}
/>
-
+
-
+
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx
index 427cdcf07..655fd34b6 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx
@@ -32,6 +32,13 @@ jest.mock('../hooks', () => ({
}));
jest.mock('./hooks', () => ({
+ videoIdChangeAlert: jest.fn().mockReturnValue({
+ videoIdChangeAlert: {
+ set: (args) => ({ set: args }),
+ show: false,
+ dismiss: (args) => ({ dismiss: args }),
+ },
+ }),
sourceHooks: jest.fn().mockReturnValue({
updateVideoId: (args) => ({ updateVideoId: args }),
updateVideoURL: jest.fn().mockName('updateVideoURL'),
@@ -81,20 +88,20 @@ describe('VideoSourceWidget', () => {
let el;
let hook;
beforeEach(() => {
+ hook = hooks.sourceHooks({ dispatch, previousVideoId: 'someVideoId', setAlert: jest.fn() });
el = shallow();
- hook = hooks.sourceHooks({ dispatch });
});
test('updateVideoId is tied to id field onBlur', () => {
const expected = hook.updateVideoId;
expect(el
// eslint-disable-next-line
- .children().at(0).children().at(0).children().at(0)
+ .children().at(1).children().at(0).children().at(0)
.props().onBlur).toEqual(expected);
});
test('updateVideoURL is tied to url field onBlur', () => {
const { onBlur } = el
// eslint-disable-next-line
- .children().at(0).children().at(0).children().at(2).props();
+ .children().at(1).children().at(0).children().at(2).props();
onBlur('onBlur event');
expect(hook.updateVideoURL).toHaveBeenCalledWith('onBlur event', '');
});
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/messages.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/messages.js
index f5a01138a..61e1d9c5d 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/messages.js
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/messages.js
@@ -28,6 +28,11 @@ const messages = defineMessages({
to an .mp4, .ogg, or .webm video file hosted elsewhere on the internet.`,
description: 'Feedback for video URL field',
},
+ videoIdChangeAlert: {
+ id: 'authoring.videoeditor.videoIdChangeAlert.message',
+ defaultMessage: 'The Video ID field has changed, please check the Video URL and fallback URL values and update them if necessary.',
+ description: 'Body message for the alert that appears when the video id has been changed.',
+ },
fallbackVideoTitle: {
id: 'authoring.videoeditor.videoSource.fallbackVideo.title',
defaultMessage: 'Fallback videos',