Add 'watch a video' button and video modal.

This commit is contained in:
Beau Carnes
2018-08-06 09:25:50 -04:00
committed by Mrugesh Mohapatra
parent 5024e69b69
commit 6cc8e5bc58
7 changed files with 103 additions and 9 deletions
+2 -1
View File
@@ -53,7 +53,8 @@ export const ChallengeNode = PropTypes.shape({
superBlock: PropTypes.string,
tail: PropTypes.arrayOf(PropTypes.string),
time: PropTypes.string,
title: PropTypes.string
title: PropTypes.string,
videoUrl: PropTypes.string
});
export const AllChallengeNode = PropTypes.shape({
@@ -13,6 +13,7 @@ import SidePanel from '../components/Side-Panel';
import Output from '../components/Output';
import CompletionModal from '../components/CompletionModal';
import HelpModal from '../components/HelpModal';
import VideoModal from '../components/VideoModal';
import ResetModal from '../components/ResetModal';
import { randomCompliment } from '../utils/get-words';
@@ -163,7 +164,8 @@ class ShowClassic extends PureComponent {
challengeType,
fields: { blockName, slug },
title,
description
description,
videoUrl
}
},
files,
@@ -223,6 +225,7 @@ class ShowClassic extends PureComponent {
guideUrl={createGuideUrl(slug)}
section={dasherize(blockName)}
title={blockNameTitle}
videoUrl={videoUrl}
/>
</ReflexElement>
<ReflexSplitter propagate={true} {...this.resizeProps} />
@@ -244,6 +247,7 @@ class ShowClassic extends PureComponent {
<CompletionModal />
<HelpModal />
<VideoModal videoUrl={videoUrl}/>
<ResetModal />
</Fragment>
);
@@ -261,6 +265,7 @@ export const query = graphql`
title
description
challengeType
videoUrl
fields {
slug
blockName
@@ -34,7 +34,8 @@ const propTypes = {
initConsole: PropTypes.func.isRequired,
section: PropTypes.string,
tests: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string
title: PropTypes.string,
videoUrl: PropTypes.string
};
export class SidePanel extends PureComponent {
@@ -42,7 +43,7 @@ export class SidePanel extends PureComponent {
super(props);
this.bindTopDiv = this.bindTopDiv.bind(this);
MathJax.Hub.Config({
tex2jax: {
tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
processEscapes: true,
processClass: 'rosetta-code'
@@ -74,16 +75,23 @@ export class SidePanel extends PureComponent {
}
render() {
const { title, description, guideUrl, tests, section } = this.props;
const {
title,
description,
guideUrl,
tests,
section,
videoUrl
} = this.props;
return (
<div className='instructions-panel' role='complementary'>
<div ref={this.bindTopDiv} />
<Spacer />
<div>
<ChallengeTitle>{title}</ChallengeTitle>
<ChallengeDescription section={section} description={description} />
<ChallengeDescription description={description} section={section} />
</div>
<ToolPanel guideUrl={guideUrl} />
<ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />
<TestSuite tests={tests} />
</div>
);
@@ -14,6 +14,7 @@ const mapDispatchToProps = dispatch =>
{
executeChallenge,
openHelpModal: () => openModal('help'),
openVideoModal: () => openModal('video'),
openResetModal: () => openModal('reset')
},
dispatch
@@ -23,14 +24,18 @@ const propTypes = {
executeChallenge: PropTypes.func.isRequired,
guideUrl: PropTypes.string,
openHelpModal: PropTypes.func.isRequired,
openResetModal: PropTypes.func.isRequired
openResetModal: PropTypes.func.isRequired,
openVideoModal: PropTypes.func.isRequired,
videoUrl: PropTypes.string
};
function ToolPanel({
executeChallenge,
openHelpModal,
openVideoModal,
openResetModal,
guideUrl
guideUrl,
videoUrl
}) {
return (
<Fragment>
@@ -57,6 +62,16 @@ function ToolPanel({
Get a hint
</Button>
) : null}
{videoUrl ? (
<Button
block={true}
bsStyle='primary'
className='btn-primary-invert'
onClick={openVideoModal}
>
Watch a video
</Button>
) : null}
<Button
block={true}
bsStyle='primary'
@@ -0,0 +1,53 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Modal } from 'react-bootstrap';
import ga from '../../../analytics';
import { closeModal, isVideoModalOpenSelector } from '../redux';
import './video-modal.css';
const mapStateToProps = state => ({ isOpen: isVideoModalOpenSelector(state) });
const mapDispatchToProps = dispatch =>
bindActionCreators(
{ closeVideoModal: () => closeModal('video') },
dispatch
);
const propTypes = {
closeVideoModal: PropTypes.func.isRequired,
isOpen: PropTypes.bool,
videoUrl: PropTypes.string
};
export class VideoModal extends PureComponent {
render() {
const { isOpen, closeVideoModal, videoUrl } = this.props;
if (isOpen) {
ga.modalview('/video-modal');
}
return (
<Modal onHide={closeVideoModal} show={isOpen} dialogClassName="video-modal">
<Modal.Header
className='help-modal-header fcc-modal'
closeButton={true}
>
<Modal.Title className='text-center'>Watch A Video</Modal.Title>
</Modal.Header>
<Modal.Body>
<iframe frameborder="0" src={videoUrl}></iframe>
<p>Tip: If the mini-browser is covering the code, click and drag to move it.
Also, feel free to stop and edit the code in the video at any time.
</p>
</Modal.Body>
</Modal>
);
}
}
VideoModal.displayName = 'VideoModal';
VideoModal.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(VideoModal);
@@ -0,0 +1,10 @@
.video-modal {
width: 90%;
}
.video-modal iframe {
width: 100%;
height: 70vh;
margin-bottom: 0px;
}
@@ -26,6 +26,7 @@ const initialState = {
modal: {
completion: false,
help: false,
video: false,
reset: false
},
projectFormVaules: {},
@@ -136,6 +137,7 @@ export const isCodeLockedSelector = state => state[ns].isCodeLocked;
export const isCompletionModalOpenSelector = state =>
state[ns].modal.completion;
export const isHelpModalOpenSelector = state => state[ns].modal.help;
export const isVideoModalOpenSelector = state => state[ns].modal.video;
export const isResetModalOpenSelector = state => state[ns].modal.reset;
export const isJSEnabledSelector = state => state[ns].isJSEnabled;
export const successMessageSelector = state => state[ns].successMessage;