diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/__snapshots__/index.test.jsx.snap
index 53fdb4043..a4c282e9a 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/__snapshots__/index.test.jsx.snap
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/__snapshots__/index.test.jsx.snap
@@ -3,7 +3,7 @@
exports[`DurationWidget render snapshots: renders as expected with default props 1`] = `
- Full video length
+ Total: 00:00:00
`;
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js
index 02707b0b6..679364f9f 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js
@@ -1,41 +1,37 @@
import { useEffect, useState } from 'react';
-import { useSelector } from 'react-redux';
-import { actions, selectors } from '../../../../../../data/redux';
import messages from '../messages';
import * as module from './hooks';
const durationMatcher = /^(\d{0,2}):?(\d{0,2})?:?(\d{0,2})?$/i;
-export const durationWidget = ({ dispatch }) => {
- const reduxStartStopTimes = useSelector(selectors.video.duration);
- const setReduxStartStopTimes = (val) => dispatch(actions.video.updateField({ duration: val }));
- const initialState = module.durationString(reduxStartStopTimes);
- const [unsavedStartStopTimes, setUnsavedStartStopTimes] = useState(initialState);
+export const durationWidget = ({ duration, updateField }) => {
+ const setDuration = (val) => updateField({ duration: val });
+ const initialState = module.durationString(duration);
+ const [unsavedDuration, setUnsavedDuration] = useState(initialState);
useEffect(() => {
- setUnsavedStartStopTimes(module.durationString(reduxStartStopTimes));
- }, [reduxStartStopTimes]);
+ setUnsavedDuration(module.durationString(duration));
+ }, [duration]);
return {
- reduxStartStopTimes,
- unsavedStartStopTimes,
+ unsavedDuration,
onBlur: (index) => (
(e) => module.updateDuration({
- reduxStartStopTimes,
- setReduxStartStopTimes,
- unsavedStartStopTimes,
- setUnsavedStartStopTimes,
+ duration,
+ setDuration,
+ unsavedDuration,
+ setUnsavedDuration,
index,
durationString: e.target.value,
})
),
onChange: (index) => (
- (e) => setUnsavedStartStopTimes(module.onDurationChange(unsavedStartStopTimes, index, e.target.value))
+ (e) => setUnsavedDuration(module.onDurationChange(unsavedDuration, index, e.target.value))
),
onKeyDown: (index) => (
- (e) => setUnsavedStartStopTimes(module.onDurationKeyDown(unsavedStartStopTimes, index, e))
+ (e) => setUnsavedDuration(module.onDurationKeyDown(unsavedDuration, index, e))
),
getTotalLabel: ({ duration, subtitle, intl }) => {
if (!duration.stopTime) {
@@ -86,23 +82,23 @@ export const durationStringFromValue = (value) => {
};
/**
- * updateDuration({ reduxStartStopTimes, unsavedStartStopTimes, setUnsavedStartStopTimes, setReduxStartStopTimes })
- * Returns a memoized callback based on inputs that updates unsavedStartStopTimes value and form value
- * if the new string is valid (reduxStartStopTimes stores a number, unsavedStartStopTimes stores a string).
- * If the duration string is invalid, resets the unsavedStartStopTimes value to the latest good value.
- * @param {object} reduxStartStopTimes - redux-stored durations in milliseconds
- * @param {object} unsavedStartStopTimes - hook-stored duration in 'hh:mm:ss' format
- * @param {func} setReduxStartStopTimes - set form value
- * @param {func} setUnsavedStartStopTimes - set unsavedStartStopTimes object
+ * updateDuration({ duration, unsavedDuration, setUnsavedDuration, setDuration })
+ * Returns a memoized callback based on inputs that updates unsavedDuration value and form value
+ * if the new string is valid (duration stores a number, unsavedDuration stores a string).
+ * If the duration string is invalid, resets the unsavedDuration value to the latest good value.
+ * @param {object} duration - redux-stored durations in milliseconds
+ * @param {object} unsavedDuration - hook-stored duration in 'hh:mm:ss' format
+ * @param {func} setDuration - set form value
+ * @param {func} setUnsavedDuration - set unsavedDuration object
* @param {string} index - startTime or stopTime
- * @return {func} - callback to update duration unsavedStartStopTimesly and in redux
+ * @return {func} - callback to update duration unsavedDurationly and in redux
* updateDuration(args)(index, durationString)
*/
export const updateDuration = ({
- reduxStartStopTimes,
- unsavedStartStopTimes,
- setReduxStartStopTimes,
- setUnsavedStartStopTimes,
+ duration,
+ unsavedDuration,
+ setDuration,
+ setUnsavedDuration,
index,
inputString,
}) => {
@@ -117,17 +113,17 @@ export const updateDuration = ({
newValue = 1000;
}
// stopTime must be at least 1 second after startTime, except 0 means no custom stopTime
- if (index === 'stopTime' && newValue > 0 && newValue < (reduxStartStopTimes.startTime + 1000)) {
- newValue = reduxStartStopTimes.startTime + 1000;
+ if (index === 'stopTime' && newValue > 0 && newValue < (duration.startTime + 1000)) {
+ newValue = duration.startTime + 1000;
}
// startTime must be at least 1 second before stopTime, except when stopTime is less than a second
// (stopTime should only be less than a second if it's zero, but we're being paranoid)
- if (index === 'startTime' && reduxStartStopTimes.stopTime >= 1000 && newValue > (reduxStartStopTimes.stopTime - 1000)) {
- newValue = reduxStartStopTimes.stopTime - 1000;
+ if (index === 'startTime' && duration.stopTime >= 1000 && newValue > (duration.stopTime - 1000)) {
+ newValue = duration.stopTime - 1000;
}
newDurationString = module.durationStringFromValue(newValue);
- setUnsavedStartStopTimes({ ...unsavedStartStopTimes, [index]: newDurationString });
- setReduxStartStopTimes({ ...reduxStartStopTimes, [index]: newValue });
+ setUnsavedDuration({ ...unsavedDuration, [index]: newDurationString });
+ setDuration({ ...duration, [index]: newValue });
};
/**
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.test.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.test.js
index 2707497b8..976ff3a80 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.test.js
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.test.js
@@ -1,40 +1,20 @@
import React from 'react';
-import { useSelector } from 'react-redux';
-import { selectors } from '../../../../../../data/redux';
import * as hooks from './hooks';
import messages from '../messages';
jest.mock('react', () => {
const updateState = jest.fn();
- const dispatchFn = jest.fn();
return {
...jest.requireActual('react'),
updateState,
useState: jest.fn(val => ([{ state: val }, (newVal) => updateState({ val, newVal })])),
useCallback: (cb, prereqs) => ({ useCallback: { cb, prereqs } }),
useEffect: jest.fn(),
- useSelector: jest.fn(),
- dispatch: dispatchFn,
- useDispatch: jest.fn(() => dispatchFn),
};
});
-jest.mock('../../../../../../data/redux', () => ({
- actions: {
- video: {
- updateField: (val) => ({ updateField: val }),
- },
- },
- selectors: {
- video: {
- duration: (state) => ({ duration: state }),
- },
- },
-}));
-
let hook;
-const dispatch = jest.fn(val => ({ dispatch: val }));
const intl = {
formatMessage: jest.fn(val => val),
};
@@ -65,35 +45,35 @@ describe('Video Settings DurationWidget hooks', () => {
jest.restoreAllMocks();
});
describe('durationWidget', () => {
- let reduxStartStopTimes;
+ const duration = {
+ startTime: '00:00:00',
+ stopTime: '00:00:10',
+ };
+ const updateField = jest.fn();
beforeEach(() => {
- hook = hooks.durationWidget({ dispatch });
- reduxStartStopTimes = useSelector(selectors.video.duration);
+ hook = hooks.durationWidget({ duration, updateField });
});
describe('behavior', () => {
describe('initialization', () => {
- test('useEffect memoized on reduxStartStopTimes', () => {
- hooks.durationWidget({ dispatch });
+ test('useEffect memoized on duration', () => {
+ hooks.durationWidget({ duration, updateField });
expect(React.useEffect).toHaveBeenCalled();
- expect(React.useEffect.mock.calls[0][1]).toEqual([reduxStartStopTimes]);
+ expect(React.useEffect.mock.calls[0][1]).toEqual([duration]);
});
- test('calls setUnsavedStartStopTimes with durationString(reduxStartStopTimes)', () => {
- hooks.durationWidget({ dispatch });
+ test('calls setUnsavedDuration with durationString(duration)', () => {
+ hooks.durationWidget({ duration, updateField });
React.useEffect.mock.calls[0][0]();
expect(React.updateState).toHaveBeenCalled();
});
});
});
describe('returns', () => {
- hook = hooks.durationWidget({ dispatch });
+ hook = hooks.durationWidget({ duration, updateField });
afterEach(() => {
jest.restoreAllMocks();
});
- describe('reduxStartStopTimes, with redux duration value', () => {
- expect(hook.reduxStartStopTimes).toEqual(useSelector(selectors.video.duration));
- });
- describe('unsavedStartStopTimes, defaulted to reduxStartStopTimes', () => {
- expect(hook.unsavedStartStopTimes).toEqual({ state: hooks.durationString(hook.reduxStartStopTimes) });
+ describe('unsavedDuration, defaulted to duration', () => {
+ expect(hook.unsavedDuration).toEqual({ state: hooks.durationString(duration) });
});
describe('onBlur, calls updateDuration', () => {
jest.spyOn(hooks, 'updateDuration').mockImplementation(jest.fn());
@@ -182,78 +162,78 @@ describe('Video Settings DurationWidget hooks', () => {
beforeEach(() => {
hook = hooks.updateDuration;
props = {
- reduxStartStopTimes: { startTime: 23000, stopTime: 600000 },
- unsavedStartStopTimes: { startTime: '00:00:23', stopTime: '00:10:00' },
- setReduxStartStopTimes: jest.fn(),
- setUnsavedStartStopTimes: jest.fn(),
+ duration: { startTime: 23000, stopTime: 600000 },
+ unsavedDuration: { startTime: '00:00:23', stopTime: '00:10:00' },
+ setDuration: jest.fn(),
+ setUnsavedDuration: jest.fn(),
index: 'startTime',
inputString: '01:23:45',
};
});
describe('if the passed durationString is valid', () => {
- it('sets the unsavedStartStopTimes to updated strings and reduxStartStopTimes to new timestamp value', () => {
+ it('sets the unsavedDuration to updated strings and duration to new timestamp value', () => {
hook({
...props,
index: testValidIndex,
inputString: testValidDuration,
});
- expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
- ...props.unsavedStartStopTimes,
+ expect(props.setUnsavedDuration).toHaveBeenCalledWith({
+ ...props.unsavedDuration,
[testValidIndex]: testValidDuration,
});
- expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
- ...props.reduxStartStopTimes,
+ expect(props.setDuration).toHaveBeenCalledWith({
+ ...props.duration,
[testValidIndex]: testValidValue,
});
});
});
describe('if the passed durationString is not valid', () => {
- it('updates unsavedStartStopTimes values to 0 (the default)', () => {
+ it('updates unsavedDuration values to 0 (the default)', () => {
hook({
...props,
index: testValidIndex,
inputString: testInvalidDuration,
});
- expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
- ...props.unsavedStartStopTimes,
+ expect(props.setUnsavedDuration).toHaveBeenCalledWith({
+ ...props.unsavedDuration,
[testValidIndex]: testValidDuration,
});
- expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
- ...props.reduxStartStopTimes,
+ expect(props.setDuration).toHaveBeenCalledWith({
+ ...props.duration,
[testValidIndex]: testValidValue,
});
});
});
describe('if the passed startTime is after (or equal to) the stored non-zero stopTime', () => {
- it('updates unsavedStartStopTimes startTime values to 1 second before stopTime', () => {
+ it('updates unsavedDuration startTime values to 1 second before stopTime', () => {
hook({
...props,
index: testValidIndex,
inputString: '00:10:00',
});
- expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
- ...props.unsavedStartStopTimes,
+ expect(props.setUnsavedDuration).toHaveBeenCalledWith({
+ ...props.unsavedDuration,
[testValidIndex]: '00:09:59',
});
- expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
- ...props.reduxStartStopTimes,
+ expect(props.setDuration).toHaveBeenCalledWith({
+ ...props.duration,
[testValidIndex]: 599000,
});
});
});
describe('if the passed stopTime is before (or equal to) the stored startTime', () => {
- it('updates unsavedStartStopTimes stopTime values to 1 second after startTime', () => {
+ it('updates unsavedDuration stopTime values to 1 second after startTime', () => {
hook({
...props,
index: testStopIndex,
inputString: '00:00:22',
});
- expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
- ...props.unsavedStartStopTimes,
+ expect(props.setUnsavedDuration).toHaveBeenCalledWith({
+ ...props.unsavedDuration,
[testStopIndex]: '00:00:24',
});
- expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
- ...props.reduxStartStopTimes,
+ expect(props.setDuration).toHaveBeenCalledWith({
+ ...props.duration,
[testStopIndex]: 24000,
});
});
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx
index 85f2c5e79..ed0ff2e26 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx
@@ -1,9 +1,11 @@
import React from 'react';
-import { useDispatch } from 'react-redux';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
import { Col, Form } from '@edx/paragon';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
+import { actions, selectors } from '../../../../../../data/redux';
import { keyStore } from '../../../../../../utils';
import CollapsibleFormWidget from '../CollapsibleFormWidget';
import hooks from './hooks';
@@ -14,28 +16,28 @@ import messages from '../messages';
* Also displays the total run time of the video.
*/
export const DurationWidget = ({
+ // redux
+ duration,
+ updateField,
// injected
intl,
}) => {
- const dispatch = useDispatch();
-
const {
- reduxStartStopTimes,
- unsavedStartStopTimes,
+ unsavedDuration,
onBlur,
onChange,
onKeyDown,
getTotalLabel,
- } = hooks.durationWidget({ dispatch });
+ } = hooks.durationWidget({ duration, updateField });
- const timeKeys = keyStore(reduxStartStopTimes);
+ const timeKeys = keyStore(duration);
return (
@@ -60,7 +62,7 @@ export const DurationWidget = ({
onBlur={onBlur(timeKeys.stopTime)}
onChange={onChange(timeKeys.stopTime)}
onKeyDown={onKeyDown(timeKeys.stopTime)}
- value={unsavedStartStopTimes.stopTime}
+ value={unsavedDuration.stopTime}
/>
@@ -69,7 +71,7 @@ export const DurationWidget = ({
{getTotalLabel({
- duration: reduxStartStopTimes,
+ duration: duration,
subtitle: false,
intl,
})}
@@ -79,8 +81,19 @@ export const DurationWidget = ({
};
DurationWidget.propTypes = {
+ // redux
+ duration: PropTypes.objectOf(PropTypes.number).isRequired,
+ updateField: PropTypes.func.isRequired,
// injected
intl: intlShape.isRequired,
};
-export default injectIntl(DurationWidget);
+export const mapStateToProps = (state) => ({
+ duration: selectors.video.duration(state),
+});
+
+export const mapDispatchToProps = {
+ updateField: actions.video.updateField,
+};
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DurationWidget));
diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx
index ab6a1da01..ddb9e8df9 100644
--- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx
+++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx
@@ -1,14 +1,30 @@
import React from 'react';
import { shallow } from 'enzyme';
+import { actions, selectors } from '../../../../../../data/redux';
import { formatMessage } from '../../../../../../../testUtils';
-import { DurationWidget } from '.';
+import { DurationWidget, mapStateToProps, mapDispatchToProps } from '.';
+
+jest.mock('../../../../../../data/redux', () => ({
+ actions: {
+ video: {
+ updateField: jest.fn().mockName('actions.video.updateField'),
+ },
+ },
+ selectors: {
+ video: {
+ duration: jest.fn(state => ({ duration: state })),
+ },
+ },
+}));
describe('DurationWidget', () => {
const props = {
- isError: false,
- subtitle: 'SuBTItle',
- title: 'tiTLE',
+ duration: {
+ startTime: '00:00:00',
+ stopTime: '00:00:10',
+ },
+ updateField: jest.fn().mockName('updateField'),
// inject
intl: { formatMessage },
};
@@ -19,4 +35,17 @@ describe('DurationWidget', () => {
).toMatchSnapshot();
});
});
+ describe('mapStateToProps', () => {
+ const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
+ test('duration from video.duration', () => {
+ expect(
+ mapStateToProps(testState).duration,
+ ).toEqual(selectors.video.duration(testState));
+ });
+ });
+ describe('mapDispatchToProps', () => {
+ test('updateField from actions.video.updateField', () => {
+ expect(mapDispatchToProps.updateField).toEqual(actions.video.updateField);
+ });
+ });
});
diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js
index 0b1bf414d..94be91054 100644
--- a/src/editors/data/services/cms/mockApi.js
+++ b/src/editors/data/services/cms/mockApi.js
@@ -286,5 +286,3 @@ export const fetchStudioView = ({ blockId, studioEndpointUrl }) => {
},
});
};
-
-export const checkTranscriptsForImport = () => mockPromise({});