feat: update onBlur to set state to default values (#148)

This commit is contained in:
Kristin Aoki
2022-11-29 15:23:14 -05:00
committed by GitHub
parent f38ae87c95
commit 7a356175e7
7 changed files with 286 additions and 221 deletions

View File

@@ -38,12 +38,12 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with
index={0}
language="en"
/>
<div
className="mb-1"
<ActionRow
className="mt-4 mb-1"
>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={[Function]}
>
<Form.Label>
@@ -54,24 +54,15 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="right"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Learners will see a link to download the transcript below the video."
description="Message for show by default checkbox"
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="right"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</div>
<IconButtonWithTooltip
alt="Learners will see a link to download the transcript below the video."
iconAs="Icon"
key="top"
tooltipContent="Learners will see a link to download the transcript below the video."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
@@ -149,12 +140,12 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with
index={1}
language="fr"
/>
<div
className="mb-1"
<ActionRow
className="mt-4 mb-1"
>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={[Function]}
>
<Form.Label>
@@ -165,24 +156,15 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="right"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Learners will see a link to download the transcript below the video."
description="Message for show by default checkbox"
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="right"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</div>
<IconButtonWithTooltip
alt="Learners will see a link to download the transcript below the video."
iconAs="Icon"
key="top"
tooltipContent="Learners will see a link to download the transcript below the video."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
@@ -256,12 +238,12 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
index={0}
language="en"
/>
<div
className="mb-1"
<ActionRow
className="mt-4 mb-1"
>
<Form.Checkbox
checked={true}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={[Function]}
>
<Form.Label>
@@ -272,24 +254,15 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="right"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Learners will see a link to download the transcript below the video."
description="Message for show by default checkbox"
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="right"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</div>
<IconButtonWithTooltip
alt="Learners will see a link to download the transcript below the video."
iconAs="Icon"
key="top"
tooltipContent="Learners will see a link to download the transcript below the video."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
@@ -427,12 +400,12 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
index={0}
language="en"
/>
<div
className="mb-1"
<ActionRow
className="mt-4 mb-1"
>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={[Function]}
>
<Form.Label>
@@ -443,24 +416,15 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="right"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Learners will see a link to download the transcript below the video."
description="Message for show by default checkbox"
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="right"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</div>
<IconButtonWithTooltip
alt="Learners will see a link to download the transcript below the video."
iconAs="Icon"
key="top"
tooltipContent="Learners will see a link to download the transcript below the video."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={true}
className="mt-4.5 decorative-control-label"
@@ -534,12 +498,12 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
index={0}
language="en"
/>
<div
className="mb-1"
<ActionRow
className="mt-4 mb-1"
>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={[Function]}
>
<Form.Label>
@@ -550,24 +514,15 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="right"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Learners will see a link to download the transcript below the video."
description="Message for show by default checkbox"
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="right"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</div>
<IconButtonWithTooltip
alt="Learners will see a link to download the transcript below the video."
iconAs="Icon"
key="top"
tooltipContent="Learners will see a link to download the transcript below the video."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={false}
className="mt-4.5 decorative-control-label"

View File

@@ -11,9 +11,9 @@ import {
Button,
Stack,
Icon,
OverlayTrigger,
Tooltip,
Alert,
IconButtonWithTooltip,
ActionRow,
} from '@edx/paragon';
import { Add, Info } from '@edx/paragon/icons';
@@ -116,28 +116,26 @@ export const TranscriptWidget = ({
index={index}
/>
))}
<div className="mb-1">
<ActionRow className="mt-4 mb-1">
<Form.Checkbox
checked={allowTranscriptDownloads}
className="mt-4.5 decorative-control-label"
className="decorative-control-label"
onChange={(e) => updateField({ allowTranscriptDownloads: e.target.checked })}
>
<Form.Label>
<FormattedMessage {...messages.allowDownloadCheckboxLabel} />
</Form.Label>
</Form.Checkbox>
<OverlayTrigger
key="right"
placement="right"
overlay={(
<Tooltip>
<FormattedMessage {...messages.tooltipMessage} />
</Tooltip>
)}
>
<Icon className="d-inline-block mx-3" src={Info} />
</OverlayTrigger>
</div>
<IconButtonWithTooltip
key="top"
tooltipPlacement="top"
tooltipContent={intl.formatMessage(messages.tooltipMessage)}
src={Info}
iconAs={Icon}
alt={intl.formatMessage(messages.tooltipMessage)}
/>
<ActionRow.Spacer />
</ActionRow>
<Form.Checkbox
checked={showTranscriptByDefault}
className="mt-4.5 decorative-control-label"

View File

@@ -10,7 +10,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
>
<Form.Control
floatingLabel="Video ID or URL"
onBlur={[MockFunction]}
onBlur={[Function]}
onChange={[MockFunction]}
value=""
/>
@@ -35,32 +35,21 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
/>
</Form.Text>
<Form.Row
className="mt-4.5"
className="mt-4"
>
<Form.Control
floatingLabel="Video URL"
/>
<OverLayTrigger
key="top"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Delete"
description="Message Presented To user for action to delete fallback video"
id="authoring.videoeditor.videoSource.deleteFallbackVideo"
/>
</ToolTip>
}
placement="top"
>
<IconButton
className="d-inline-block"
iconAs="Icon"
onClick={[Function]}
/>
</OverLayTrigger>
<IconButtonWithTooltip
alt="Delete"
iconAs="Icon"
key="top-delete-somEUrL"
onClick={[Function]}
tooltipContent="Delete"
tooltipPlacement="top"
/>
</Form.Row>
<Form.Row
<ActionRow
className="mt-4"
>
<Form.Checkbox
@@ -76,26 +65,19 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default
/>
</Form.Label>
</Form.Checkbox>
<OverLayTrigger
key="top"
overlay={
<ToolTip>
<FormattedMessage
defaultMessage="Allow learners to download versions of this video in
<IconButtonWithTooltip
alt="Allow learners to download versions of this video in
different formats if they cannot use the edX video player or do not have
access to YouTube."
description="Message for allow video downloads checkbox"
id="authoring.videoeditor.videoSource.fallbackVideo.allowDownloadTooltipMessage"
/>
</ToolTip>
}
placement="top"
>
<Icon
className="d-inline-block mx-3"
/>
</OverLayTrigger>
</Form.Row>
iconAs="Icon"
key="top"
tooltipContent="Allow learners to download versions of this video in
different formats if they cannot use the edX video player or do not have
access to YouTube."
tooltipPlacement="top"
/>
<ActionRow.Spacer />
</ActionRow>
</Form.Group>
<Button
onClick={[Function]}

View File

@@ -1,25 +0,0 @@
import { actions } from '../../../../../../data/redux';
/**
* deleteFallbackVideo({ fallbackVideos, dispatch })(videoUrl)
* deleteFallbackVideo takes the current array of fallback videos, string of
* deleted video URL and dispatch method, and updates the redux value for
* fallbackVideos.
* @param {array} fallbackVideos - array of current fallback videos
* @param {func} dispatch - redux dispatch method
* @param {string} videoUrl - string of the video URL for the fallabck video that needs to be deleted
*/
export const deleteFallbackVideo = ({ fallbackVideos, dispatch }) => (videoUrl) => {
const updatedFallbackVideos = [];
let firstOccurence = true;
fallbackVideos.forEach(item => {
if (item === videoUrl && firstOccurence) {
firstOccurence = false;
} else {
updatedFallbackVideos.push(item);
}
});
dispatch(actions.video.updateField({ fallbackVideos: updatedFallbackVideos }));
};
export default { deleteFallbackVideo };

View File

@@ -0,0 +1,66 @@
import { actions } from '../../../../../../data/redux';
import { isEdxVideo } from '../../../../../../data/services/cms/api';
/**
* updateVideoType({ dispatch })({e, source})
* updateVideoType takes the current onBlur event, the current object of the video
* source, and dispatch method, and updates the redux value for all the fields to
* their default values except videoId, fallbackVideos, videoType, and handouts.
* @param {event} e - object for onBlur event
* @param {func} dispatch - redux dispatch method
* @param {object} source - object for the Video Source field functions and values
*/
export const updateVideoType = ({ dispatch }) => ({ e, source }) => {
source.onBlur(e);
let videoType;
let videoId;
if (isEdxVideo(source.local)) {
videoType = 'edxVideo';
videoId = source.local;
} else if (source.local.includes('youtu.be')) {
videoType = 'youtube';
videoId = '';
} else {
videoType = 'html5source';
videoId = '';
}
dispatch(actions.video.updateField({
videoId,
videoType,
allowVideoDownloads: false,
thumbnail: null,
transcripts: [],
allowTranscriptDownloads: false,
showTranscriptByDefault: false,
duration: {
startTime: '00:00:00',
stopTime: '00:00:00',
total: '00:00:00',
},
licenseType: null,
}));
};
/**
* deleteFallbackVideo({ fallbackVideos, dispatch })(videoUrl)
* deleteFallbackVideo takes the current array of fallback videos, string of
* deleted video URL and dispatch method, and updates the redux value for
* fallbackVideos.
* @param {array} fallbackVideos - array of current fallback videos
* @param {func} dispatch - redux dispatch method
* @param {string} videoUrl - string of the video URL for the fallabck video that needs to be deleted
*/
export const deleteFallbackVideo = ({ fallbackVideos, dispatch }) => (videoUrl) => {
const updatedFallbackVideos = [];
let firstOccurence = true;
fallbackVideos.forEach(item => {
if (item === videoUrl && firstOccurence) {
firstOccurence = false;
} else {
updatedFallbackVideos.push(item);
}
});
dispatch(actions.video.updateField({ fallbackVideos: updatedFallbackVideos }));
};
export default { deleteFallbackVideo };

View File

@@ -0,0 +1,100 @@
import { dispatch } from 'react-redux';
import { actions } from '../../../../../../data/redux';
import * as hooks from './hooks';
jest.mock('react-redux', () => {
const dispatchFn = jest.fn();
return {
...jest.requireActual('react-redux'),
dispatch: dispatchFn,
useDispatch: jest.fn(() => dispatchFn),
};
});
jest.mock('../../../../../../data/redux', () => ({
actions: {
video: {
updateField: jest.fn(),
},
},
}));
describe('VideoEditorHandout hooks', () => {
describe('updateVideoType', () => {
const sourceEdxVideo = {
onBlur: jest.fn(),
local: '06b1503a-7df4-4e72-b970-326e02dbcbe4',
};
const sourceYouTube = {
onBlur: jest.fn(),
local: 'youtu.be',
};
const sourceHtml5Source = {
onBlur: jest.fn(),
local: 'sOMEranDomfILe.mp4',
};
const mockState = {
videoId: '',
videoType: '',
allowVideoDownloads: false,
thumbnail: null,
transcripts: [],
allowTranscriptDownloads: false,
showTranscriptByDefault: false,
duration: {
startTime: '00:00:00',
stopTime: '00:00:00',
total: '00:00:00',
},
licenseType: null,
};
it('returns dispatches updateField action with default state and edxVideo Id', () => {
hooks.updateVideoType({ dispatch })({ e: { target: { value: sourceEdxVideo.local } }, source: sourceEdxVideo });
expect(dispatch).toHaveBeenCalledWith(
actions.video.updateField({
...mockState,
videoId: sourceEdxVideo.local,
videoType: 'edxVideo',
}),
);
});
it('returns dispatches updateField action with default state and YouTube video', () => {
hooks.updateVideoType({ dispatch })({
e: { target: { value: sourceYouTube.local } },
source: sourceYouTube,
});
expect(dispatch).toHaveBeenCalledWith(
actions.video.updateField({
...mockState,
videoId: sourceYouTube.local,
}),
);
});
it('returns dispatches updateField action with default state and html5source video', () => {
hooks.updateVideoType({ dispatch })({
e: { target: { value: sourceHtml5Source.local } },
source: sourceHtml5Source,
});
expect(dispatch).toHaveBeenCalledWith(
actions.video.updateField({
...mockState,
videoId: sourceHtml5Source.local,
}),
);
});
});
describe('deleteFallbackVideo', () => {
const videoUrl = 'sOmERAndoMuRl1';
const fallbackVideos = ['sOmERAndoMuRl1', 'sOmERAndoMuRl2', 'sOmERAndoMuRl1', ''];
const updatedFallbackVideos = ['sOmERAndoMuRl2', 'sOmERAndoMuRl1', ''];
it('returns dispatches updateField action with updatedFallbackVideos', () => {
hooks.deleteFallbackVideo({ fallbackVideos, dispatch })(videoUrl);
expect(dispatch).toHaveBeenCalledWith(
actions.video.updateField({
fallbackVideos: updatedFallbackVideos,
}),
);
});
});
});

View File

@@ -4,10 +4,9 @@ import PropTypes from 'prop-types';
import {
Form,
IconButton,
IconButtonWithTooltip,
ActionRow,
Icon,
OverlayTrigger,
Tooltip,
Button,
} from '@edx/paragon';
import { Delete, Info, Add } from '@edx/paragon/icons';
@@ -47,6 +46,7 @@ export const VideoSourceWidget = ({
},
});
const deleteFallbackVideo = module.deleteFallbackVideo({ fallbackVideos: fallbackVideos.formValue, dispatch });
const updateVideoType = module.updateVideoType({ dispatch });
return (
<CollapsibleFormWidget
@@ -57,7 +57,7 @@ export const VideoSourceWidget = ({
<Form.Control
floatingLabel={intl.formatMessage(messages.videoIdOrUrlLabel)}
onChange={source.onChange}
onBlur={source.onBlur}
onBlur={(e) => updateVideoType({ e, source })}
value={source.local}
/>
</div>
@@ -68,32 +68,25 @@ export const VideoSourceWidget = ({
<FormattedMessage {...messages.fallbackVideoMessage} />
</Form.Text>
{fallbackVideos.formValue.map((videoUrl, index) => (
<Form.Row className="mt-4.5">
<Form.Row className="mt-4">
<Form.Control
floatingLabel={intl.formatMessage(messages.fallbackVideoLabel)}
onChange={fallbackVideos.onChange(index)}
value={fallbackVideos.local[index]}
onBlur={fallbackVideos.onBlur(index)}
/>
<OverlayTrigger
key="top"
placement="top"
overlay={(
<Tooltip>
<FormattedMessage {...messages.deleteFallbackVideo} />
</Tooltip>
)}
>
<IconButton
className="d-inline-block"
iconAs={Icon}
src={Delete}
onClick={() => deleteFallbackVideo(videoUrl)}
/>
</OverlayTrigger>
<IconButtonWithTooltip
key={`top-delete-${videoUrl}`}
tooltipPlacement="top"
tooltipContent={intl.formatMessage(messages.deleteFallbackVideo)}
src={Delete}
iconAs={Icon}
alt={intl.formatMessage(messages.deleteFallbackVideo)}
onClick={() => deleteFallbackVideo(videoUrl)}
/>
</Form.Row>
))}
<Form.Row className="mt-4">
<ActionRow className="mt-4">
<Form.Checkbox
checked={allowDownload.local}
className="decorative-control-label"
@@ -103,18 +96,16 @@ export const VideoSourceWidget = ({
<FormattedMessage {...messages.allowDownloadCheckboxLabel} />
</Form.Label>
</Form.Checkbox>
<OverlayTrigger
<IconButtonWithTooltip
key="top"
placement="top"
overlay={(
<Tooltip>
<FormattedMessage {...messages.tooltipMessage} />
</Tooltip>
)}
>
<Icon className="d-inline-block mx-3" src={Info} />
</OverlayTrigger>
</Form.Row>
tooltipPlacement="top"
tooltipContent={intl.formatMessage(messages.tooltipMessage)}
src={Info}
iconAs={Icon}
alt={intl.formatMessage(messages.tooltipMessage)}
/>
<ActionRow.Spacer />
</ActionRow>
</Form.Group>
<Button
iconBefore={Add}
@@ -126,8 +117,6 @@ export const VideoSourceWidget = ({
</CollapsibleFormWidget>
);
};
VideoSourceWidget.defaultProps = {
};
VideoSourceWidget.propTypes = {
// injected
intl: intlShape.isRequired,