feat: add social share widget (#323)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SocialShareWidget rendered with videoSharingEnabled true and allowVideoSharing value equals false should have subtitle with text that reads Enabled 1`] = `
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
fontSize="x-small"
|
||||
subtitle="Disabled"
|
||||
title="Social Sharing"
|
||||
>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow this video to be shareable to social media"
|
||||
description="Description for sociail sharing setting"
|
||||
id="authoring.videoeditor.socialShare.description"
|
||||
/>
|
||||
</div>
|
||||
<Form.Checkbox
|
||||
checked={false}
|
||||
className="mt-3"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
>
|
||||
<div
|
||||
className="small text-gray-700"
|
||||
>
|
||||
This video is shareable to social media
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="Note: This setting is overridden by the course outline page."
|
||||
description="Message that the setting can be overriden in the course outline"
|
||||
id="authoring.videoeditor.socialShare.overrideNote"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="mt-3"
|
||||
>
|
||||
<Hyperlink
|
||||
className="text-primary-500"
|
||||
destination="sOMeURl.cOM"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more about social sharing
|
||||
</Hyperlink>
|
||||
</div>
|
||||
</injectIntl(ShimmedIntlComponent)>
|
||||
`;
|
||||
|
||||
exports[`SocialShareWidget rendered with videoSharingEnabled true and allowVideoSharing value equals true should have subtitle with text that reads Enabled 1`] = `
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
fontSize="x-small"
|
||||
subtitle="Enabled"
|
||||
title="Social Sharing"
|
||||
>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow this video to be shareable to social media"
|
||||
description="Description for sociail sharing setting"
|
||||
id="authoring.videoeditor.socialShare.description"
|
||||
/>
|
||||
</div>
|
||||
<Form.Checkbox
|
||||
checked={true}
|
||||
className="mt-3"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
>
|
||||
<div
|
||||
className="small text-gray-700"
|
||||
>
|
||||
This video is shareable to social media
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="Note: This setting is overridden by the course outline page."
|
||||
description="Message that the setting can be overriden in the course outline"
|
||||
id="authoring.videoeditor.socialShare.overrideNote"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="mt-3"
|
||||
>
|
||||
<Hyperlink
|
||||
className="text-primary-500"
|
||||
destination="sOMeURl.cOM"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more about social sharing
|
||||
</Hyperlink>
|
||||
</div>
|
||||
</injectIntl(ShimmedIntlComponent)>
|
||||
`;
|
||||
|
||||
exports[`SocialShareWidget rendered with with videoSharingEnabled false should return null 1`] = `""`;
|
||||
@@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
FormattedMessage,
|
||||
injectIntl,
|
||||
intlShape,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Hyperlink,
|
||||
Form,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import { selectors, actions } from '../../../../../../data/redux';
|
||||
import CollapsibleFormWidget from '../CollapsibleFormWidget';
|
||||
import messages from './messages';
|
||||
|
||||
/**
|
||||
* Collapsible Form widget controlling video thumbnail
|
||||
*/
|
||||
export const SocialShareWidget = ({
|
||||
// injected
|
||||
intl,
|
||||
// redux
|
||||
allowVideoSharing,
|
||||
videoSharingEnabledForCourse,
|
||||
videoSharingLearnMoreLink,
|
||||
updateField,
|
||||
}) => {
|
||||
const isSetByCourse = allowVideoSharing.level === 'course';
|
||||
const getSubtitle = () => {
|
||||
if (allowVideoSharing.value) {
|
||||
return intl.formatMessage(messages.enabledSubtitle);
|
||||
}
|
||||
return intl.formatMessage(messages.disabledSubtitle);
|
||||
};
|
||||
|
||||
return (videoSharingEnabledForCourse ? (
|
||||
<CollapsibleFormWidget
|
||||
fontSize="x-small"
|
||||
title={intl.formatMessage(messages.title)}
|
||||
subtitle={getSubtitle()}
|
||||
>
|
||||
<div>
|
||||
<FormattedMessage {...messages.socialSharingDescription} />
|
||||
</div>
|
||||
<Form.Checkbox
|
||||
className="mt-3"
|
||||
checked={allowVideoSharing.value}
|
||||
disabled={isSetByCourse}
|
||||
onChange={(e) => updateField({
|
||||
allowVideoSharing: {
|
||||
...allowVideoSharing,
|
||||
value: e.target.checked,
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div className="small text-gray-700">
|
||||
{intl.formatMessage(messages.socialSharingCheckboxLabel)}
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<div>
|
||||
<FormattedMessage {...messages.overrideSocialSharingNote} />
|
||||
</div>
|
||||
{isSetByCourse && (
|
||||
<div>
|
||||
<FormattedMessage {...messages.disclaimerSettingLocation} />
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-3">
|
||||
<Hyperlink className="text-primary-500" destination={videoSharingLearnMoreLink} target="_blank">
|
||||
{intl.formatMessage(messages.learnMoreLinkLabel)}
|
||||
</Hyperlink>
|
||||
</div>
|
||||
</CollapsibleFormWidget>
|
||||
) : null);
|
||||
};
|
||||
|
||||
SocialShareWidget.defaultProps = {
|
||||
allowVideoSharing: {
|
||||
level: 'block',
|
||||
value: false,
|
||||
},
|
||||
videoSharingEnabledForCourse: false,
|
||||
};
|
||||
|
||||
SocialShareWidget.propTypes = {
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
allowVideoSharing: PropTypes.shape({
|
||||
level: PropTypes.string.isRequired,
|
||||
value: PropTypes.bool.isRequired,
|
||||
}),
|
||||
videoSharingEnabledForCourse: PropTypes.bool,
|
||||
videoSharingLearnMoreLink: PropTypes.string.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
allowVideoSharing: selectors.video.allowVideoSharing(state),
|
||||
videoSharingLearnMoreLink: selectors.video.videoSharingLearnMoreLink(state),
|
||||
videoSharingEnabledForCourse: selectors.video.videoSharingEnabledForCourse(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = (dispatch) => ({
|
||||
updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)),
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SocialShareWidget));
|
||||
@@ -0,0 +1,185 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { formatMessage } from '../../../../../../../testUtils';
|
||||
import { actions, selectors } from '../../../../../../data/redux';
|
||||
import { SocialShareWidget, mapStateToProps, mapDispatchToProps } from '.';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('react', () => {
|
||||
const updateState = jest.fn();
|
||||
return {
|
||||
...jest.requireActual('react'),
|
||||
updateState,
|
||||
useContext: jest.fn(() => ({ license: ['error.license', jest.fn().mockName('error.setLicense')] })),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../../data/redux', () => ({
|
||||
actions: {
|
||||
video: {
|
||||
updateField: jest.fn().mockName('actions.video.updateField'),
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
video: {
|
||||
allowVideoSharing: jest.fn(state => ({ allowVideoSharing: state })),
|
||||
videoSharingEnabledForCourse: jest.fn(state => ({ videoSharingEnabledForCourse: state })),
|
||||
videoSharingLearnMoreLink: jest.fn(state => ({ videoSharingLearnMoreLink: state })),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('SocialShareWidget', () => {
|
||||
const props = {
|
||||
title: 'tiTLE',
|
||||
intl: { formatMessage },
|
||||
videoSharingEnabledForCourse: false,
|
||||
allowVideoSharing: {
|
||||
level: 'block',
|
||||
value: false,
|
||||
},
|
||||
videoSharingLearnMoreLink: 'sOMeURl.cOM',
|
||||
updateField: jest.fn().mockName('args.updateField'),
|
||||
};
|
||||
|
||||
describe('rendered with with videoSharingEnabled false', () => {
|
||||
it('should return null', () => {
|
||||
const wrapper = shallow(<SocialShareWidget {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendered with videoSharingEnabled true', () => {
|
||||
describe('and allowVideoSharing value equals true', () => {
|
||||
describe(' with level equal to course', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'course',
|
||||
value: true,
|
||||
}}
|
||||
/>);
|
||||
it('should have setting location message', () => {
|
||||
const settingLocationDisclaimer = wrapper.find('FormattedMessage').at(2).prop('defaultMessage');
|
||||
expect(settingLocationDisclaimer).toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
});
|
||||
it('should have checkbox disabled prop equal true', () => {
|
||||
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
|
||||
expect(disabledCheckbox).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe(' with level equal to block', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'block',
|
||||
value: true,
|
||||
}}
|
||||
/>);
|
||||
it('should not have setting location message', () => {
|
||||
const formattedMessages = wrapper.find('FormattedMessage');
|
||||
expect(formattedMessages.length).toEqual(2);
|
||||
expect(formattedMessages.at(0)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
expect(formattedMessages.at(1)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
});
|
||||
it('should have checkbox disabled prop equal false', () => {
|
||||
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
|
||||
expect(disabledCheckbox).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('should have subtitle with text that reads Enabled', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'block',
|
||||
value: true,
|
||||
}}
|
||||
/>);
|
||||
const subtitle = wrapper.prop('subtitle');
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(subtitle).toEqual('Enabled');
|
||||
});
|
||||
});
|
||||
describe('and allowVideoSharing value equals false', () => {
|
||||
describe(' with level equal to course', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'course',
|
||||
value: false,
|
||||
}}
|
||||
/>);
|
||||
it('should have setting location message', () => {
|
||||
const settingLocationDisclaimer = wrapper.find('FormattedMessage').at(2).prop('defaultMessage');
|
||||
expect(settingLocationDisclaimer).toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
});
|
||||
it('should have checkbox disabled prop equal true', () => {
|
||||
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
|
||||
expect(disabledCheckbox).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe(' with level equal to block', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'block',
|
||||
value: false,
|
||||
}}
|
||||
/>);
|
||||
it('should not have setting location message', () => {
|
||||
const formattedMessages = wrapper.find('FormattedMessage');
|
||||
expect(formattedMessages.length).toEqual(2);
|
||||
expect(formattedMessages.at(0)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
expect(formattedMessages.at(1)).not.toEqual(messages.disclaimerSettingLocation.defaultMessage);
|
||||
});
|
||||
it('should have checkbox disabled prop equal false', () => {
|
||||
const disabledCheckbox = wrapper.children().at(1).prop('disabled');
|
||||
expect(disabledCheckbox).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('should have subtitle with text that reads Enabled', () => {
|
||||
const wrapper = shallow(<SocialShareWidget
|
||||
{...props}
|
||||
videoSharingEnabledForCourse
|
||||
allowVideoSharing={{
|
||||
level: 'block',
|
||||
value: false,
|
||||
}}
|
||||
/>);
|
||||
const subtitle = wrapper.prop('subtitle');
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(subtitle).toEqual('Disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
test('allowVideoSharing from video.allowVideoSharing', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).allowVideoSharing,
|
||||
).toEqual(selectors.video.allowVideoSharing(testState));
|
||||
});
|
||||
test('videoSharingEnabledForCourse from video.videoSharingEnabledForCourse', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).videoSharingEnabledForCourse,
|
||||
).toEqual(selectors.video.videoSharingEnabledForCourse(testState));
|
||||
});
|
||||
test('videoSharingLearnMoreLink from video.videoSharingLearnMoreLink', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).videoSharingLearnMoreLink,
|
||||
).toEqual(selectors.video.videoSharingLearnMoreLink(testState));
|
||||
});
|
||||
});
|
||||
describe('mapDispatchToProps', () => {
|
||||
const dispatch = jest.fn();
|
||||
test('updateField from actions.video.updateField', () => {
|
||||
expect(mapDispatchToProps.updateField).toEqual(dispatch(actions.video.updateField));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
id: 'authoring.videoeditor.socialShare.title',
|
||||
defaultMessage: 'Social Sharing',
|
||||
description: 'Title for socialShare widget',
|
||||
},
|
||||
disabledSubtitle: {
|
||||
id: 'authoring.videoeditor.socialShare.disabled.subtitle',
|
||||
defaultMessage: 'Disabled',
|
||||
description: 'Subtitle for unavailable socialShare widget',
|
||||
},
|
||||
enabledSubtitle: {
|
||||
id: 'authoring.videoeditor.socialShare.enabled.subtitle',
|
||||
defaultMessage: 'Enabled',
|
||||
description: 'Subtitle for when thumbnail has been uploaded to the widget',
|
||||
},
|
||||
learnMoreLinkLabel: {
|
||||
id: 'authoring.videoeditor.socialShare.learnMore.link',
|
||||
defaultMessage: 'Learn more about social sharing',
|
||||
description: 'Text for link to learn more about social sharing',
|
||||
},
|
||||
socialSharingDescription: {
|
||||
id: 'authoring.videoeditor.socialShare.description',
|
||||
defaultMessage: 'Allow this video to be shareable to social media',
|
||||
description: 'Description for sociail sharing setting',
|
||||
},
|
||||
socialSharingCheckboxLabel: {
|
||||
id: 'authoring.videoeditor.socialShare.checkbox.label',
|
||||
defaultMessage: 'This video is shareable to social media',
|
||||
description: 'Label for checkbox for allowing video to be share',
|
||||
},
|
||||
overrideSocialSharingNote: {
|
||||
id: 'authoring.videoeditor.socialShare.overrideNote',
|
||||
defaultMessage: 'Note: This setting is overridden by the course outline page.',
|
||||
description: 'Message that the setting can be overriden in the course outline',
|
||||
},
|
||||
disclaimerSettingLocation: {
|
||||
id: 'authoring.videoeditor.socialShare.settingsDisclaimer',
|
||||
defaultMessage: 'Change this setting on the course outline page.',
|
||||
description: 'Message for disabled checkbox that notifies user that setting can be modified in course outline',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -287,53 +287,6 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with videoSh
|
||||
</OverlayTrigger>
|
||||
<ActionRow.Spacer />
|
||||
</ActionRow>
|
||||
<ActionRow
|
||||
className="mt-4.5"
|
||||
>
|
||||
<Form.Checkbox
|
||||
checked={false}
|
||||
className="decorative-control-label"
|
||||
onChange={[MockFunction]}
|
||||
>
|
||||
<div
|
||||
className="small text-gray-700"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow this video to be shared on social media."
|
||||
description="Label for allow shareable video checkbox"
|
||||
id="authoring.videoeditor.videoSource.allowVideoSharingCheckboxLabel"
|
||||
/>
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<OverlayTrigger
|
||||
key="top-allow-sharing"
|
||||
overlay={
|
||||
<Tooltip
|
||||
id="tooltip-top-allow-sharing"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow learners to share this video publicly on social media.
|
||||
The video will be viewable by anyone, they will not need to enroll in the course
|
||||
or even have an edX account. Links to the course about page and to enroll in the
|
||||
course will appear alongside the video."
|
||||
description="Message for allow shareable video checkbox"
|
||||
id="authoring.videoeditor.videoSource.allowVideoSharingTooltipMessage"
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Icon
|
||||
style={
|
||||
Object {
|
||||
"height": "16px",
|
||||
"width": "16px",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<ActionRow.Spacer />
|
||||
</ActionRow>
|
||||
</Form.Group>
|
||||
<div
|
||||
className="my-4 border-primary-100 border-bottom"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import {
|
||||
Form,
|
||||
@@ -21,7 +20,6 @@ import {
|
||||
import * as widgetHooks from '../hooks';
|
||||
import * as hooks from './hooks';
|
||||
import messages from './messages';
|
||||
import { selectors } from '../../../../../../data/redux';
|
||||
|
||||
import { ErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert';
|
||||
import CollapsibleFormWidget from '../CollapsibleFormWidget';
|
||||
@@ -32,8 +30,6 @@ import CollapsibleFormWidget from '../CollapsibleFormWidget';
|
||||
export const VideoSourceWidget = ({
|
||||
// injected
|
||||
intl,
|
||||
// redux
|
||||
videoSharingEnabledForCourse,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
@@ -41,7 +37,6 @@ export const VideoSourceWidget = ({
|
||||
videoSource: source,
|
||||
fallbackVideos,
|
||||
allowVideoDownloads: allowDownload,
|
||||
allowVideoSharing: allowSharing,
|
||||
} = widgetHooks.widgetValues({
|
||||
dispatch,
|
||||
fields: {
|
||||
@@ -49,7 +44,6 @@ export const VideoSourceWidget = ({
|
||||
[widgetHooks.selectorKeys.videoId]: widgetHooks.genericWidget,
|
||||
[widgetHooks.selectorKeys.fallbackVideos]: widgetHooks.arrayWidget,
|
||||
[widgetHooks.selectorKeys.allowVideoDownloads]: widgetHooks.genericWidget,
|
||||
[widgetHooks.selectorKeys.allowVideoSharing]: widgetHooks.genericWidget,
|
||||
},
|
||||
});
|
||||
const { videoIdChangeAlert } = hooks.videoIdChangeAlert();
|
||||
@@ -144,31 +138,6 @@ export const VideoSourceWidget = ({
|
||||
</OverlayTrigger>
|
||||
<ActionRow.Spacer />
|
||||
</ActionRow>
|
||||
{videoSharingEnabledForCourse && (
|
||||
<ActionRow className="mt-4.5">
|
||||
<Form.Checkbox
|
||||
checked={allowSharing.local}
|
||||
className="decorative-control-label"
|
||||
onChange={allowSharing.onCheckedChange}
|
||||
>
|
||||
<div className="small text-gray-700">
|
||||
<FormattedMessage {...messages.allowVideoSharingCheckboxLabel} />
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<OverlayTrigger
|
||||
key="top-allow-sharing"
|
||||
placement="top"
|
||||
overlay={(
|
||||
<Tooltip id="tooltip-top-allow-sharing">
|
||||
<FormattedMessage {...messages.allowVideoSharingTooltipMessage} />
|
||||
</Tooltip>
|
||||
)}
|
||||
>
|
||||
<Icon src={InfoOutline} style={{ height: '16px', width: '16px' }} />
|
||||
</OverlayTrigger>
|
||||
<ActionRow.Spacer />
|
||||
</ActionRow>
|
||||
)}
|
||||
</Form.Group>
|
||||
<div className="my-4 border-primary-100 border-bottom" />
|
||||
<Button
|
||||
@@ -186,12 +155,6 @@ export const VideoSourceWidget = ({
|
||||
VideoSourceWidget.propTypes = {
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
videoSharingEnabledForCourse: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
videoSharingEnabledForCourse: selectors.video.videoSharingEnabledForCourse(state),
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, {})(VideoSourceWidget));
|
||||
export default injectIntl(VideoSourceWidget);
|
||||
|
||||
@@ -10,12 +10,14 @@ import TranscriptWidget from './components/TranscriptWidget';
|
||||
import VideoSourceWidget from './components/VideoSourceWidget';
|
||||
import VideoPreviewWidget from './components/VideoPreviewWidget';
|
||||
import './index.scss';
|
||||
import SocialShareWidget from './components/SocialShareWidget';
|
||||
|
||||
export const VideoSettingsModal = () => (
|
||||
<>
|
||||
<ErrorSummary />
|
||||
<VideoPreviewWidget />
|
||||
<VideoSourceWidget />
|
||||
<SocialShareWidget />
|
||||
<ThumbnailWidget />
|
||||
<TranscriptWidget />
|
||||
<DurationWidget />
|
||||
|
||||
@@ -8,8 +8,9 @@ import { parseYoutubeId } from '../../services/cms/api';
|
||||
|
||||
export const loadVideoData = () => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const rawVideoData = state.app.blockValue.data.metadata ? state.app.blockValue.data.metadata : {};
|
||||
const courseLicenseData = state.app.courseDetails.data ? state.app.courseDetails.data : {};
|
||||
const blockValueData = state.app.blockValue.data;
|
||||
const rawVideoData = blockValueData.metadata ? blockValueData.metadata : {};
|
||||
const courseData = state.app.courseDetails.data ? state.app.courseDetails.data : {};
|
||||
const studioView = state.app.studioView?.data?.html;
|
||||
const {
|
||||
videoId,
|
||||
@@ -23,16 +24,21 @@ export const loadVideoData = () => (dispatch, getState) => {
|
||||
const [licenseType, licenseOptions] = module.parseLicense({ licenseData: studioView, level: 'block' });
|
||||
const transcripts = module.parseTranscripts({ transcriptsData: studioView });
|
||||
const [courseLicenseType, courseLicenseDetails] = module.parseLicense({
|
||||
licenseData: courseLicenseData.license,
|
||||
licenseData: courseData.license,
|
||||
level: 'course',
|
||||
});
|
||||
|
||||
const allowVideoSharing = module.parseVideoSharingSetting({
|
||||
courseSetting: blockValueData?.video_sharing_options,
|
||||
blockSetting: rawVideoData.public_access,
|
||||
});
|
||||
dispatch(actions.video.load({
|
||||
videoSource: videoUrl || '',
|
||||
videoId,
|
||||
fallbackVideos,
|
||||
allowVideoDownloads: rawVideoData.download_video,
|
||||
allowVideoSharing: rawVideoData.public_access,
|
||||
allowVideoSharing,
|
||||
videoSharingLearnMoreLink: blockValueData?.video_sharing_doc_url,
|
||||
videoSharingEnabledForCourse: blockValueData?.video_sharing_enabled,
|
||||
transcripts,
|
||||
allowTranscriptDownloads: rawVideoData.download_track,
|
||||
showTranscriptByDefault: rawVideoData.show_captions,
|
||||
@@ -61,7 +67,6 @@ export const loadVideoData = () => (dispatch, getState) => {
|
||||
dispatch(requests.fetchVideoFeatures({
|
||||
onSuccess: (response) => dispatch(actions.video.updateField({
|
||||
allowThumbnailUpload: response.data.allowThumbnailUpload,
|
||||
videoSharingEnabledForCourse: response.data.videoSharingEnabled,
|
||||
})),
|
||||
}));
|
||||
const youTubeId = parseYoutubeId(videoUrl);
|
||||
@@ -100,6 +105,19 @@ export const determineVideoSources = ({
|
||||
};
|
||||
};
|
||||
|
||||
export const parseVideoSharingSetting = ({ courseSetting, blockSetting }) => {
|
||||
switch (courseSetting) {
|
||||
case 'all-on':
|
||||
return { level: 'course', value: true };
|
||||
case 'all-off':
|
||||
return { level: 'course', value: false };
|
||||
case 'per-video':
|
||||
return { level: 'block', value: blockSetting };
|
||||
default:
|
||||
return { level: 'block', value: blockSetting };
|
||||
}
|
||||
};
|
||||
|
||||
export const parseTranscripts = ({ transcriptsData }) => {
|
||||
if (!transcriptsData) {
|
||||
return [];
|
||||
|
||||
@@ -53,7 +53,6 @@ const mockAllowTranscriptImport = { data: { command: 'import' } };
|
||||
const mockVideoFeatures = {
|
||||
data: {
|
||||
allowThumbnailUpload: 'soMEbOolEAn',
|
||||
videoSharingEnabled: 'someBOoOoOlean',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -70,6 +69,10 @@ const testMetadata = {
|
||||
transcripts: ['do', 're', 'mi'],
|
||||
thumbnail: 'thuMBNaIl',
|
||||
};
|
||||
const videoSharingData = {
|
||||
video_sharing_doc_url: 'SomEUrL.Com',
|
||||
video_sharing_options: 'OpTIOns',
|
||||
};
|
||||
const testState = {
|
||||
transcripts: ['la'],
|
||||
thumbnail: 'sOMefILE',
|
||||
@@ -92,7 +95,7 @@ describe('video thunkActions', () => {
|
||||
getState = jest.fn(() => ({
|
||||
app: {
|
||||
blockId: 'soMEBloCk',
|
||||
blockValue: { data: { metadata: { ...testMetadata } } },
|
||||
blockValue: { data: { metadata: { ...testMetadata }, ...videoSharingData } },
|
||||
studioEndpointUrl: 'soMEeNDPoiNT',
|
||||
courseDetails: { data: { license: null } },
|
||||
studioView: { data: { html: 'sOMeHTml' } },
|
||||
@@ -110,6 +113,10 @@ describe('video thunkActions', () => {
|
||||
videoId: 'videOiD',
|
||||
fallbackVideos: 'fALLbACKvIDeos',
|
||||
});
|
||||
jest.spyOn(thunkActions, thunkActionsKeys.parseVideoSharingSetting).mockReturnValue({
|
||||
level: 'course',
|
||||
value: true,
|
||||
});
|
||||
jest.spyOn(thunkActions, thunkActionsKeys.parseLicense).mockReturnValue([
|
||||
'liCENSEtyPe',
|
||||
{
|
||||
@@ -146,6 +153,12 @@ describe('video thunkActions', () => {
|
||||
videoId: 'videOiD',
|
||||
fallbackVideos: 'fALLbACKvIDeos',
|
||||
allowVideoDownloads: testMetadata.download_video,
|
||||
allowVideoSharing: {
|
||||
level: 'course',
|
||||
value: true,
|
||||
},
|
||||
videoSharingLearnMoreLink: videoSharingData.video_sharing_doc_url,
|
||||
videoSharingEnableForCourse: videoSharingData.video_sharing_enabled,
|
||||
transcripts: testMetadata.transcripts,
|
||||
allowTranscriptDownloads: testMetadata.download_track,
|
||||
showTranscriptByDefault: testMetadata.show_captions,
|
||||
@@ -177,10 +190,8 @@ describe('video thunkActions', () => {
|
||||
dispatchedAction1.fetchVideoFeatures.onSuccess(mockVideoFeatures);
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.video.updateField({
|
||||
allowThumbnailUpload: mockVideoFeatures.data.allowThumbnailUpload,
|
||||
videoSharingEnabledForCourse: mockVideoFeatures.data.videoSharingEnabled,
|
||||
}));
|
||||
dispatch.mockClear();
|
||||
|
||||
dispatchedAction2.checkTranscriptsForImport.onSuccess(mockAllowTranscriptImport);
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.video.updateField({
|
||||
allowTranscriptImport: true,
|
||||
@@ -349,6 +360,55 @@ describe('video thunkActions', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('parseVideoShareSetting', () => {
|
||||
describe('has no course setting or block setting for video sharing', () => {
|
||||
it('should return an object with level equal to block and value equal to null', () => {
|
||||
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
|
||||
courseSetting: null,
|
||||
blockSetting: null,
|
||||
});
|
||||
expect(videoSharingSetting).toEqual({ level: 'block', value: null });
|
||||
});
|
||||
});
|
||||
describe('has no course setting and block setting defined for video sharing', () => {
|
||||
it('should return an object with level equal to block and value equal to true', () => {
|
||||
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
|
||||
courseSetting: null,
|
||||
blockSetting: true,
|
||||
});
|
||||
expect(videoSharingSetting).toEqual({ level: 'block', value: true });
|
||||
});
|
||||
});
|
||||
describe('has course setting defined for video sharing', () => {
|
||||
describe('course setting equals all-on', () => {
|
||||
it('should return an object with level equal to course and value equal to true', () => {
|
||||
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
|
||||
courseSetting: 'all-on',
|
||||
blockSetting: true,
|
||||
});
|
||||
expect(videoSharingSetting).toEqual({ level: 'course', value: true });
|
||||
});
|
||||
});
|
||||
describe('course setting equals all-off', () => {
|
||||
it('should return an object with level equal to course and value equal to false', () => {
|
||||
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
|
||||
courseSetting: 'all-off',
|
||||
blockSetting: true,
|
||||
});
|
||||
expect(videoSharingSetting).toEqual({ level: 'course', value: false });
|
||||
});
|
||||
});
|
||||
describe('course setting equals per-video', () => {
|
||||
it('should return an object with level equal to block and value equal to block setting', () => {
|
||||
const videoSharingSetting = thunkActions.parseVideoSharingSetting({
|
||||
courseSetting: 'per-video',
|
||||
blockSetting: false,
|
||||
});
|
||||
expect(videoSharingSetting).toEqual({ level: 'block', value: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('uploadHandout', () => {
|
||||
beforeEach(() => {
|
||||
thunkActions.uploadHandout({ file: mockFilename })(dispatch);
|
||||
|
||||
@@ -10,8 +10,12 @@ const initialState = {
|
||||
'',
|
||||
],
|
||||
allowVideoDownloads: false,
|
||||
allowVideoSharing: false,
|
||||
allowVideoSharing: {
|
||||
level: 'block',
|
||||
value: false,
|
||||
},
|
||||
videoSharingEnabledForCourse: false,
|
||||
videoSharingLearnMoreLink: '',
|
||||
thumbnail: null,
|
||||
transcripts: [],
|
||||
allowTranscriptDownloads: false,
|
||||
|
||||
@@ -18,6 +18,7 @@ export const simpleSelectors = [
|
||||
stateKeys.fallbackVideos,
|
||||
stateKeys.allowVideoDownloads,
|
||||
stateKeys.videoSharingEnabledForCourse,
|
||||
stateKeys.videoSharingLearnMoreLink,
|
||||
stateKeys.allowVideoSharing,
|
||||
stateKeys.thumbnail,
|
||||
stateKeys.transcripts,
|
||||
|
||||
@@ -166,7 +166,7 @@ export const apiMethods = {
|
||||
metadata: {
|
||||
display_name: title,
|
||||
download_video: content.allowVideoDownloads,
|
||||
public_access: content.allowVideoSharing,
|
||||
public_access: content.allowVideoSharing.value,
|
||||
edx_video_id: edxVideoId,
|
||||
html5_sources: html5Sources,
|
||||
youtube_id_1_0: youtubeId,
|
||||
|
||||
@@ -125,7 +125,10 @@ describe('cms api', () => {
|
||||
videoSource: 'viDeOSouRCE',
|
||||
fallbackVideos: 'FalLBacKVidEOs',
|
||||
allowVideoDownloads: 'alLOwViDeodownLOads',
|
||||
allowVideoSharing: 'alloWviDeOshArinG',
|
||||
allowVideoSharing: {
|
||||
level: 'sOMeStRInG',
|
||||
value: 'alloWviDeOshArinG',
|
||||
},
|
||||
thumbnail: 'THUmbNaIL',
|
||||
transcripts: 'traNScRiPts',
|
||||
allowTranscriptDownloads: 'aLloWTRaNScriPtdoWnlOADS',
|
||||
@@ -162,7 +165,7 @@ describe('cms api', () => {
|
||||
metadata: {
|
||||
display_name: title,
|
||||
download_video: content.allowVideoDownloads,
|
||||
public_access: content.allowVideoSharing,
|
||||
public_access: content.allowVideoSharing.value,
|
||||
edx_video_id: edxVideoId,
|
||||
html5_sources: html5Sources,
|
||||
youtube_id_1_0: youtubeId,
|
||||
|
||||
Reference in New Issue
Block a user