feat: add alert to notify video id changes (#295)
This commit is contained in:
@@ -5,6 +5,17 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
|
||||
fontSize="x-small"
|
||||
title="Video source"
|
||||
>
|
||||
<ErrorAlert
|
||||
dismissError={[Function]}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
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."
|
||||
id="authoring.videoeditor.videoIdChangeAlert.message"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<Form.Group>
|
||||
<div
|
||||
className="border-primary-100 border-bottom pb-4"
|
||||
@@ -15,7 +26,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
|
||||
onChange={[MockFunction]}
|
||||
value=""
|
||||
/>
|
||||
<Component
|
||||
<Form.Control.Feedback
|
||||
className="text-primary-300 mb-4"
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -23,14 +34,14 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
|
||||
description="Feedback for video ID field"
|
||||
id="authoring.videoeditor.videoSource.videoId.feedback"
|
||||
/>
|
||||
</Component>
|
||||
</Form.Control.Feedback>
|
||||
<Form.Control
|
||||
floatingLabel="Video URL"
|
||||
onBlur={[Function]}
|
||||
onChange={[MockFunction]}
|
||||
value=""
|
||||
/>
|
||||
<Component
|
||||
<Form.Control.Feedback
|
||||
className="text-primary-300"
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -39,7 +50,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
|
||||
description="Feedback for video URL field"
|
||||
id="authoring.videoeditor.videoSource.videoUrl.feedback"
|
||||
/>
|
||||
</Component>
|
||||
</Form.Control.Feedback>
|
||||
</div>
|
||||
<div
|
||||
className="mt-4"
|
||||
@@ -147,6 +158,17 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
|
||||
fontSize="x-small"
|
||||
title="Video source"
|
||||
>
|
||||
<ErrorAlert
|
||||
dismissError={[Function]}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
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."
|
||||
id="authoring.videoeditor.videoIdChangeAlert.message"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<Form.Group>
|
||||
<div
|
||||
className="border-primary-100 border-bottom pb-4"
|
||||
@@ -157,7 +179,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
|
||||
onChange={[MockFunction]}
|
||||
value=""
|
||||
/>
|
||||
<Component
|
||||
<Form.Control.Feedback
|
||||
className="text-primary-300 mb-4"
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -165,14 +187,14 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
|
||||
description="Feedback for video ID field"
|
||||
id="authoring.videoeditor.videoSource.videoId.feedback"
|
||||
/>
|
||||
</Component>
|
||||
</Form.Control.Feedback>
|
||||
<Form.Control
|
||||
floatingLabel="Video URL"
|
||||
onBlur={[Function]}
|
||||
onChange={[MockFunction]}
|
||||
value=""
|
||||
/>
|
||||
<Component
|
||||
<Form.Control.Feedback
|
||||
className="text-primary-300"
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -181,7 +203,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
|
||||
description="Feedback for video URL field"
|
||||
id="authoring.videoeditor.videoSource.videoUrl.feedback"
|
||||
/>
|
||||
</Component>
|
||||
</Form.Control.Feedback>
|
||||
</div>
|
||||
<div
|
||||
className="mt-4"
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import React from 'react';
|
||||
import { actions } from '../../../../../../data/redux';
|
||||
import { parseYoutubeId } from '../../../../../../data/services/cms/api';
|
||||
import * as requests from '../../../../../../data/redux/thunkActions/requests';
|
||||
|
||||
export const sourceHooks = ({ dispatch }) => ({
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)}
|
||||
>
|
||||
<ErrorAlert
|
||||
dismissError={videoIdChangeAlert.dismiss}
|
||||
hideHeading
|
||||
isError={videoIdChangeAlert.show}
|
||||
>
|
||||
<FormattedMessage {...messages.videoIdChangeAlert} />
|
||||
</ErrorAlert>
|
||||
<Form.Group>
|
||||
<div className="border-primary-100 border-bottom pb-4">
|
||||
<Form.Control
|
||||
@@ -71,18 +83,18 @@ export const VideoSourceWidget = ({
|
||||
onBlur={updateVideoId}
|
||||
value={videoId.local}
|
||||
/>
|
||||
<FormControlFeedback className="text-primary-300 mb-4">
|
||||
<Form.Control.Feedback className="text-primary-300 mb-4">
|
||||
<FormattedMessage {...messages.videoIdFeedback} />
|
||||
</FormControlFeedback>
|
||||
</Form.Control.Feedback>
|
||||
<Form.Control
|
||||
floatingLabel={intl.formatMessage(messages.videoUrlLabel)}
|
||||
onChange={source.onChange}
|
||||
onBlur={(e) => updateVideoURL(e, videoId.local)}
|
||||
value={source.local}
|
||||
/>
|
||||
<FormControlFeedback className="text-primary-300">
|
||||
<Form.Control.Feedback className="text-primary-300">
|
||||
<FormattedMessage {...messages.videoUrlFeedback} />
|
||||
</FormControlFeedback>
|
||||
</Form.Control.Feedback>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<FormattedMessage {...messages.fallbackVideoTitle} />
|
||||
|
||||
@@ -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(<VideoSourceWidget {...props} />);
|
||||
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', '');
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user