feat(client): migrate to ts - (HelpModal, Preview, VideoModal, Side-Panel) (#42857)

* refactor: migrate HelpModal, Preview, VideoModal, Side-Panel

* refactor: import order

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Igor Cunha
2021-09-17 13:37:53 -04:00
committed by GitHub
parent 5de0aae5da
commit 2dd106eb2f
13 changed files with 332 additions and 337 deletions

View File

@ -9,3 +9,19 @@ declare module '*.svg' {
const content: string; const content: string;
export default content; export default content;
} }
declare namespace NodeJS {
interface Global {
MathJax: {
Hub: {
Config: (attributes: {
tex2jax: {
inlineMath: Array<string[]>;
processEscapes: boolean;
processClass: string;
};
}) => void;
Queue: (attributes: unknown[]) => void;
};
};
}
}

View File

@ -1,4 +1,3 @@
// Package Utilities
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
@ -8,49 +7,47 @@ import { HandlerProps } from 'react-reflex';
import Media from 'react-responsive'; import Media from 'react-responsive';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { createStructuredSelector } from 'reselect'; import { createStructuredSelector } from 'reselect';
// Local Utilities
import store from 'store'; import store from 'store';
import { challengeTypes } from '../../../../utils/challenge-types'; import { challengeTypes } from '../../../../utils/challenge-types';
import LearnLayout from '../../../components/layouts/learn'; import LearnLayout from '../../../components/layouts/learn';
import { import {
ChallengeNodeType,
ChallengeFiles,
ChallengeFile, ChallengeFile,
ChallengeFiles,
ChallengeMetaType, ChallengeMetaType,
Test, ChallengeNodeType,
ResizePropsType ResizePropsType,
Test
} from '../../../redux/prop-types'; } from '../../../redux/prop-types';
import { isContained } from '../../../utils/is-contained'; import { isContained } from '../../../utils/is-contained';
import ChallengeDescription from '../components/Challenge-Description'; import ChallengeDescription from '../components/Challenge-Description';
import HelpModal from '../components/HelpModal';
import Hotkeys from '../components/Hotkeys'; import Hotkeys from '../components/Hotkeys';
import Preview from '../components/Preview';
import ResetModal from '../components/ResetModal'; import ResetModal from '../components/ResetModal';
import SidePanel from '../components/Side-Panel';
import VideoModal from '../components/VideoModal';
import ChallengeTitle from '../components/challenge-title'; import ChallengeTitle from '../components/challenge-title';
import CompletionModal from '../components/completion-modal'; import CompletionModal from '../components/completion-modal';
import HelpModal from '../components/help-modal';
import Output from '../components/output'; import Output from '../components/output';
import Preview from '../components/preview';
import SidePanel from '../components/side-panel';
import VideoModal from '../components/video-modal';
import { import {
createFiles, cancelTests,
challengeFilesSelector, challengeFilesSelector,
challengeMounted,
challengeTestsSelector, challengeTestsSelector,
consoleOutputSelector,
createFiles,
executeChallenge,
initConsole, initConsole,
initTests, initTests,
updateChallengeMeta, isChallengeCompletedSelector,
challengeMounted, updateChallengeMeta
consoleOutputSelector,
executeChallenge,
cancelTests,
isChallengeCompletedSelector
} from '../redux'; } from '../redux';
import { getGuideUrl } from '../utils'; import { getGuideUrl } from '../utils';
import DesktopLayout from './DesktopLayout'; import DesktopLayout from './DesktopLayout';
import MobileLayout from './MobileLayout'; import MobileLayout from './MobileLayout';
import MultifileEditor from './MultifileEditor'; import MultifileEditor from './MultifileEditor';
// Styles
import './classic.css'; import './classic.css';
import '../components/test-frame.css'; import '../components/test-frame.css';
@ -124,7 +121,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
static displayName: string; static displayName: string;
containerRef: React.RefObject<unknown>; containerRef: React.RefObject<unknown>;
editorRef: React.RefObject<unknown>; editorRef: React.RefObject<unknown>;
instructionsPanelRef: React.RefObject<HTMLElement>; instructionsPanelRef: React.RefObject<HTMLDivElement>;
resizeProps: ResizePropsType; resizeProps: ResizePropsType;
constructor(props: ShowClassicProps) { constructor(props: ShowClassicProps) {
@ -309,7 +306,6 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
{title} {title}
</ChallengeTitle> </ChallengeTitle>
} }
className='full-height'
guideUrl={getGuideUrl({ forumTopicId, title })} guideUrl={getGuideUrl({ forumTopicId, title })}
instructionsPanelRef={this.instructionsPanelRef} instructionsPanelRef={this.instructionsPanelRef}
showToolPanel={showToolPanel} showToolPanel={showToolPanel}

View File

@ -1,90 +0,0 @@
import { Button, Modal } from '@freecodecamp/react-bootstrap';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Trans, withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import envData from '../../../../../config/env.json';
import { executeGA } from '../../../redux';
import { createQuestion, closeModal, isHelpModalOpenSelector } from '../redux';
import './help-modal.css';
const { forumLocation } = envData;
const mapStateToProps = state => ({ isOpen: isHelpModalOpenSelector(state) });
const mapDispatchToProps = dispatch =>
bindActionCreators(
{ createQuestion, executeGA, closeHelpModal: () => closeModal('help') },
dispatch
);
const propTypes = {
closeHelpModal: PropTypes.func.isRequired,
createQuestion: PropTypes.func.isRequired,
executeGA: PropTypes.func,
isOpen: PropTypes.bool,
t: PropTypes.func.isRequired
};
const RSA = forumLocation + '/t/19514';
export class HelpModal extends Component {
render() {
const { isOpen, closeHelpModal, createQuestion, executeGA, t } = this.props;
if (isOpen) {
executeGA({ type: 'modal', data: '/help-modal' });
}
return (
<Modal dialogClassName='help-modal' onHide={closeHelpModal} show={isOpen}>
<Modal.Header
className='help-modal-header fcc-modal'
closeButton={true}
>
<Modal.Title className='text-center'>
{t('buttons.ask-for-help')}
</Modal.Title>
</Modal.Header>
<Modal.Body className='help-modal-body text-center'>
<h3>
<Trans i18nKey='learn.tried-rsa'>
<a
href={RSA}
rel='noopener noreferrer'
target='_blank'
title={t('learn.rsa')}
>
placeholder
</a>
</Trans>
</h3>
<Button
block={true}
bsSize='lg'
bsStyle='primary'
onClick={createQuestion}
>
{t('buttons.create-post')}
</Button>
<Button
block={true}
bsSize='lg'
bsStyle='primary'
onClick={closeHelpModal}
>
{t('buttons.cancel')}
</Button>
</Modal.Body>
</Modal>
);
}
}
HelpModal.displayName = 'HelpModal';
HelpModal.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(withTranslation()(HelpModal));

View File

@ -1,66 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { previewMounted } from '../redux';
import './preview.css';
const mainId = 'fcc-main-frame';
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
previewMounted
},
dispatch
);
const propTypes = {
className: PropTypes.string,
disableIframe: PropTypes.bool,
previewMounted: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
class Preview extends Component {
constructor(...props) {
super(...props);
this.state = {
iframeStatus: props.disableIframe
};
}
componentDidMount() {
this.props.previewMounted();
}
componentDidUpdate(prevProps) {
if (this.props.disableIframe !== prevProps.disableIframe) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ iframeStatus: !this.state.iframeStatus });
}
}
render() {
const { t } = this.props;
const iframeToggle = this.state.iframeStatus ? 'disable' : 'enable';
return (
<div className={`notranslate challenge-preview ${iframeToggle}-iframe`}>
<iframe
className={'challenge-preview-frame'}
id={mainId}
title={t('learn.chal-preview')}
/>
</div>
);
}
}
Preview.displayName = 'Preview';
Preview.propTypes = propTypes;
export default connect(null, mapDispatchToProps)(withTranslation()(Preview));

View File

@ -1,81 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { mathJaxScriptLoader } from '../../../utils/script-loaders';
import { challengeTestsSelector } from '../redux';
import TestSuite from './Test-Suite';
import ToolPanel from './Tool-Panel';
import './side-panel.css';
const mapStateToProps = createSelector(challengeTestsSelector, tests => ({
tests
}));
const propTypes = {
block: PropTypes.string,
challengeDescription: PropTypes.element.isRequired,
challengeTitle: PropTypes.element.isRequired,
guideUrl: PropTypes.string,
instructionsPanelRef: PropTypes.any.isRequired,
showToolPanel: PropTypes.bool,
tests: PropTypes.arrayOf(PropTypes.object),
videoUrl: PropTypes.string
};
export class SidePanel extends Component {
componentDidMount() {
const MathJax = global.MathJax;
const mathJaxMountPoint = document.querySelector('#mathjax');
const mathJaxChallenge =
this.props.block === 'rosetta-code' ||
this.props.block === 'project-euler';
if (MathJax) {
// Configure MathJax when it's loaded and
// users navigate from another challenge
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
['$', '$'],
['\\(', '\\)']
],
processEscapes: true,
processClass: 'rosetta-code|project-euler'
}
});
MathJax.Hub.Queue([
'Typeset',
MathJax.Hub,
document.querySelector('.rosetta-code'),
document.querySelector('.project-euler')
]);
} else if (!mathJaxMountPoint && mathJaxChallenge) {
mathJaxScriptLoader();
}
}
render() {
const { instructionsPanelRef, guideUrl, tests, showToolPanel, videoUrl } =
this.props;
return (
<div
className='instructions-panel'
ref={instructionsPanelRef}
role='complementary'
tabIndex='-1'
>
{this.props.challengeTitle}
{this.props.challengeDescription}
{showToolPanel && <ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />}
<TestSuite tests={tests} />
</div>
);
}
}
SidePanel.displayName = 'SidePanel';
SidePanel.propTypes = propTypes;
export default connect(mapStateToProps)(SidePanel);

View File

@ -1,67 +0,0 @@
import { Modal } from '@freecodecamp/react-bootstrap';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { executeGA } from '../../../redux';
import { closeModal, isVideoModalOpenSelector } from '../redux';
import './video-modal.css';
const mapStateToProps = state => ({ isOpen: isVideoModalOpenSelector(state) });
const mapDispatchToProps = dispatch =>
bindActionCreators(
{ closeVideoModal: () => closeModal('video'), executeGA },
dispatch
);
const propTypes = {
closeVideoModal: PropTypes.func.isRequired,
executeGA: PropTypes.func,
isOpen: PropTypes.bool,
t: PropTypes.func.isRequired,
videoUrl: PropTypes.string
};
export class VideoModal extends Component {
render() {
const { isOpen, closeVideoModal, videoUrl, executeGA, t } = this.props;
if (isOpen) {
executeGA({ type: 'modal', data: '/completion-modal' });
}
return (
<Modal
dialogClassName='video-modal'
onHide={closeVideoModal}
show={isOpen}
>
<Modal.Header
className='video-modal-header fcc-modal'
closeButton={true}
>
<Modal.Title className='text-center'>
{t('buttons.watch-video')}
</Modal.Title>
</Modal.Header>
<Modal.Body className='video-modal-body'>
<iframe
frameBorder='0'
src={videoUrl}
title={t('buttons.watch-video')}
/>
<p>{t('learn.scrimba-tip')}</p>
</Modal.Body>
</Modal>
);
}
}
VideoModal.displayName = 'VideoModal';
VideoModal.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(withTranslation()(VideoModal));

View File

@ -0,0 +1,90 @@
import { Button, Modal } from '@freecodecamp/react-bootstrap';
import React from 'react';
import { Trans, withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import envData from '../../../../../config/env.json';
import { executeGA } from '../../../redux';
import { createQuestion, closeModal, isHelpModalOpenSelector } from '../redux';
import './help-modal.css';
interface HelpModalProps {
closeHelpModal: () => void;
createQuestion: () => void;
executeGA: (attributes: { type: string; data: string }) => void;
isOpen?: boolean;
t: (text: string) => string;
}
const { forumLocation } = envData;
const mapStateToProps = (state: unknown) => ({
isOpen: isHelpModalOpenSelector(state) as boolean
});
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators(
{ createQuestion, executeGA, closeHelpModal: () => closeModal('help') },
dispatch
);
const RSA = forumLocation + '/t/19514';
export function HelpModal({
closeHelpModal,
createQuestion,
executeGA,
isOpen,
t
}: HelpModalProps): JSX.Element {
if (isOpen) {
executeGA({ type: 'modal', data: '/help-modal' });
}
return (
<Modal dialogClassName='help-modal' onHide={closeHelpModal} show={isOpen}>
<Modal.Header className='help-modal-header fcc-modal' closeButton={true}>
<Modal.Title className='text-center'>
{t('buttons.ask-for-help')}
</Modal.Title>
</Modal.Header>
<Modal.Body className='help-modal-body text-center'>
<h3>
<Trans i18nKey='learn.tried-rsa'>
<a
href={RSA}
rel='noopener noreferrer'
target='_blank'
title={t('learn.rsa')}
>
placeholder
</a>
</Trans>
</h3>
<Button
block={true}
bsSize='lg'
bsStyle='primary'
onClick={createQuestion}
>
{t('buttons.create-post')}
</Button>
<Button
block={true}
bsSize='lg'
bsStyle='primary'
onClick={closeHelpModal}
>
{t('buttons.cancel')}
</Button>
</Modal.Body>
</Modal>
);
}
HelpModal.displayName = 'HelpModal';
export default connect(
mapStateToProps,
mapDispatchToProps
)(withTranslation()(HelpModal));

View File

@ -0,0 +1,52 @@
import React, { useState, useEffect } from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { previewMounted } from '../redux';
import './preview.css';
const mainId = 'fcc-main-frame';
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators(
{
previewMounted
},
dispatch
);
interface PreviewProps {
className?: string;
disableIframe?: boolean;
previewMounted: () => void;
t: (text: string) => string;
}
function Preview({ disableIframe, previewMounted, t }: PreviewProps) {
const [iframeStatus, setIframeStatus] = useState<boolean | undefined>(false);
const iframeToggle = iframeStatus ? 'disable' : 'enable';
useEffect(() => {
previewMounted();
}, [previewMounted]);
useEffect(() => {
setIframeStatus(disableIframe);
}, [disableIframe]);
return (
<div className={`notranslate challenge-preview ${iframeToggle}-iframe`}>
<iframe
className={'challenge-preview-frame'}
id={mainId}
title={t('learn.chal-preview')}
/>
</div>
);
}
Preview.displayName = 'Preview';
export default connect(null, mapDispatchToProps)(withTranslation()(Preview));

View File

@ -0,0 +1,86 @@
import React, { useEffect, ReactElement } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { mathJaxScriptLoader } from '../../../utils/script-loaders';
import { challengeTestsSelector } from '../redux';
import TestSuite from './Test-Suite';
import ToolPanel from './Tool-Panel';
import './side-panel.css';
const mapStateToProps = createSelector(
challengeTestsSelector,
(tests: Record<string, unknown>[]) => ({
tests
})
);
interface SidePanelProps {
block: string;
challengeDescription: ReactElement;
challengeTitle: ReactElement;
guideUrl?: string;
instructionsPanelRef: React.RefObject<HTMLDivElement>;
showToolPanel: boolean;
tests?: Record<string, unknown>[];
videoUrl?: string;
}
export function SidePanel({
block,
challengeDescription,
challengeTitle,
guideUrl,
instructionsPanelRef,
showToolPanel = false,
tests,
videoUrl
}: SidePanelProps): JSX.Element {
useEffect(() => {
const MathJax = global.MathJax;
const mathJaxMountPoint = document.querySelector('#mathjax');
const mathJaxChallenge =
block === 'rosetta-code' || block === 'project-euler';
if (MathJax) {
// Configure MathJax when it's loaded and
// users navigate from another challenge
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
['$', '$'],
['\\(', '\\)']
],
processEscapes: true,
processClass: 'rosetta-code|project-euler'
}
});
MathJax.Hub.Queue([
'Typeset',
MathJax.Hub,
document.querySelector('.rosetta-code'),
document.querySelector('.project-euler')
]);
} else if (!mathJaxMountPoint && mathJaxChallenge) {
mathJaxScriptLoader();
}
}, [block]);
return (
<div
className='instructions-panel'
ref={instructionsPanelRef}
role='complementary'
tabIndex={-1}
>
{challengeTitle}
{challengeDescription}
{/* @ts-expect-error ToolPanel's redux props are being inferred here, but we don't need to provide them here */}
{showToolPanel && <ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />}
<TestSuite tests={tests} />
</div>
);
}
SidePanel.displayName = 'SidePanel';
export default connect(mapStateToProps)(SidePanel);

View File

@ -0,0 +1,64 @@
import { Modal } from '@freecodecamp/react-bootstrap';
import React from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { executeGA } from '../../../redux';
import { closeModal, isVideoModalOpenSelector } from '../redux';
import './video-modal.css';
interface VideoModalProps {
closeVideoModal: () => void;
executeGA: (attributes: { type: string; data: string }) => void;
isOpen?: boolean;
t: (attribute: string) => string;
videoUrl?: string;
}
const mapStateToProps = (state: unknown) => ({
isOpen: isVideoModalOpenSelector(state) as boolean
});
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators(
{ closeVideoModal: () => closeModal('video'), executeGA },
dispatch
);
export function VideoModal({
closeVideoModal,
executeGA,
isOpen,
t,
videoUrl
}: VideoModalProps): JSX.Element {
if (isOpen) {
executeGA({ type: 'modal', data: '/completion-modal' });
}
return (
<Modal dialogClassName='video-modal' onHide={closeVideoModal} show={isOpen}>
<Modal.Header className='video-modal-header fcc-modal' closeButton={true}>
<Modal.Title className='text-center'>
{t('buttons.watch-video')}
</Modal.Title>
</Modal.Header>
<Modal.Body className='video-modal-body'>
<iframe
frameBorder='0'
src={videoUrl}
title={t('buttons.watch-video')}
/>
<p>{t('learn.scrimba-tip')}</p>
</Modal.Body>
</Modal>
);
}
VideoModal.displayName = 'VideoModal';
export default connect(
mapStateToProps,
mapDispatchToProps
)(withTranslation()(VideoModal));

View File

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
// Package Utilities
import { Grid, Col, Row } from '@freecodecamp/react-bootstrap'; import { Col, Grid, Row } from '@freecodecamp/react-bootstrap';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
@ -10,28 +10,26 @@ import { TFunction, withTranslation } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
// Local Utilities
import Spacer from '../../../../components/helpers/spacer'; import Spacer from '../../../../components/helpers/spacer';
import LearnLayout from '../../../../components/layouts/learn'; import LearnLayout from '../../../../components/layouts/learn';
import { isSignedInSelector } from '../../../../redux'; import { isSignedInSelector } from '../../../../redux';
import { import {
ChallengeNodeType,
ChallengeMetaType, ChallengeMetaType,
ChallengeNodeType,
Test Test
} from '../../../../redux/prop-types'; } from '../../../../redux/prop-types';
import ChallengeDescription from '../../components/Challenge-Description'; import ChallengeDescription from '../../components/Challenge-Description';
import HelpModal from '../../components/HelpModal';
import Hotkeys from '../../components/Hotkeys'; import Hotkeys from '../../components/Hotkeys';
import TestSuite from '../../components/Test-Suite'; import TestSuite from '../../components/Test-Suite';
import ChallengeTitle from '../../components/challenge-title'; import ChallengeTitle from '../../components/challenge-title';
import CompletionModal from '../../components/completion-modal'; import CompletionModal from '../../components/completion-modal';
import HelpModal from '../../components/help-modal';
import Output from '../../components/output'; import Output from '../../components/output';
import { import {
executeChallenge,
challengeMounted, challengeMounted,
challengeTestsSelector, challengeTestsSelector,
consoleOutputSelector, consoleOutputSelector,
executeChallenge,
initConsole, initConsole,
initTests, initTests,
isChallengeCompletedSelector, isChallengeCompletedSelector,
@ -42,7 +40,6 @@ import { getGuideUrl } from '../../utils';
import SolutionForm from '../solution-form'; import SolutionForm from '../solution-form';
import ProjectToolPanel from '../tool-panel'; import ProjectToolPanel from '../tool-panel';
// Styles
import '../../components/test-frame.css'; import '../../components/test-frame.css';
// Redux Setup // Redux Setup

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
// Package Utilities
import { Grid, Col, Row } from '@freecodecamp/react-bootstrap'; import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import React, { Component } from 'react'; import React, { Component } from 'react';
@ -11,7 +10,6 @@ import { bindActionCreators } from 'redux';
import type { Dispatch } from 'redux'; import type { Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
// Local Utilities
import Spacer from '../../../../components/helpers/spacer'; import Spacer from '../../../../components/helpers/spacer';
import LearnLayout from '../../../../components/layouts/learn'; import LearnLayout from '../../../../components/layouts/learn';
import { import {
@ -19,10 +17,10 @@ import {
ChallengeMetaType ChallengeMetaType
} from '../../../../redux/prop-types'; } from '../../../../redux/prop-types';
import ChallengeDescription from '../../components/Challenge-Description'; import ChallengeDescription from '../../components/Challenge-Description';
import HelpModal from '../../components/HelpModal';
import Hotkeys from '../../components/Hotkeys'; import Hotkeys from '../../components/Hotkeys';
import ChallengeTitle from '../../components/challenge-title'; import ChallengeTitle from '../../components/challenge-title';
import CompletionModal from '../../components/completion-modal'; import CompletionModal from '../../components/completion-modal';
import HelpModal from '../../components/help-modal';
import { import {
challengeMounted, challengeMounted,
isChallengeCompletedSelector, isChallengeCompletedSelector,

View File

@ -9,7 +9,7 @@ const backend = path.resolve(
); );
const classic = path.resolve( const classic = path.resolve(
__dirname, __dirname,
'../../src/templates/Challenges/classic/Show.tsx' '../../src/templates/Challenges/classic/show.tsx'
); );
const frontend = path.resolve( const frontend = path.resolve(
__dirname, __dirname,