From 918d5a160d3ace1a2a6a049a69b1adf5b71a73a1 Mon Sep 17 00:00:00 2001 From: Tom <20648924+moT01@users.noreply.github.com> Date: Fri, 25 Jun 2021 09:59:33 -0500 Subject: [PATCH] feat(client): ts-migrate challenge templates/show components (#42553) --- client/src/redux/prop-types.ts | 34 ++++- .../Challenges/classic/{Show.js => Show.tsx} | 129 ++++++++++-------- .../Challenges/codeally/{show.js => show.tsx} | 39 +++--- .../components/Challenge-Description.tsx | 6 +- .../components/{Hotkeys.js => Hotkeys.tsx} | 59 ++++---- .../{ResetModal.js => ResetModal.tsx} | 37 +++-- .../projects/backend/{Show.js => Show.tsx} | 98 ++++++++----- .../projects/frontend/{Show.js => Show.tsx} | 72 ++++++---- .../Challenges/video/{Show.js => Show.tsx} | 86 +++++++----- client/utils/gatsby/challengePageCreator.js | 10 +- 10 files changed, 343 insertions(+), 227 deletions(-) rename client/src/templates/Challenges/classic/{Show.js => Show.tsx} (82%) rename client/src/templates/Challenges/codeally/{show.js => show.tsx} (74%) rename client/src/templates/Challenges/components/{Hotkeys.js => Hotkeys.tsx} (61%) rename client/src/templates/Challenges/components/{ResetModal.js => ResetModal.tsx} (75%) rename client/src/templates/Challenges/projects/backend/{Show.js => Show.tsx} (75%) rename client/src/templates/Challenges/projects/frontend/{Show.js => Show.tsx} (75%) rename client/src/templates/Challenges/video/{Show.js => Show.tsx} (81%) diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index 045042ed1b..d668274d87 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -160,7 +160,9 @@ export type ChallengeNodeType = { fields: { slug: string; blockName: string; + tests: TestType[]; }; + files: ChallengeFilesType; forumTopicId: number; guideUrl: string; head: string[]; @@ -171,6 +173,11 @@ export type ChallengeNodeType = { isLocked: boolean; isPrivate: boolean; order: number; + question: { + text: string; + answers: string[]; + solution: number; + }; required: [ { link: string; @@ -184,6 +191,8 @@ export type ChallengeNodeType = { time: string; title: string; translationPending: boolean; + url: string; + videoId: string; videoUrl: string; }; @@ -204,7 +213,7 @@ export type AllMarkdownRemarkType = { }; export type ResizePropsType = { - onStopResize: () => void; + onStopResize: (arg0: React.ChangeEvent) => void; onResize: () => void; }; @@ -289,6 +298,28 @@ export type ChallengeFileType = { export type ExtTypes = 'js' | 'html' | 'css' | 'jsx'; export type FileKeyTypes = 'indexjs' | 'indexhtml' | 'indexcss'; +export type ChallengeFilesType = + | { + indexcss: ChallengeFileType; + indexhtml: ChallengeFileType; + indexjs: ChallengeFileType; + indexjsx: ChallengeFileType; + } + | Record; + +export type ChallengeMetaType = { + block: string; + id: string; + introPath: string; + nextChallengePath: string; + prevChallengePath: string; + removeComments: boolean; + superBlock: string; + title?: string; + challengeType?: number; + helpCategory: string; +}; + export type PortfolioType = { id: string; title?: string; @@ -297,6 +328,7 @@ export type PortfolioType = { description?: string; }; +// This looks redundant - same as ChallengeNodeType above? export type ChallengeNode = { block: string; challengeOrder: number; diff --git a/client/src/templates/Challenges/classic/Show.js b/client/src/templates/Challenges/classic/Show.tsx similarity index 82% rename from client/src/templates/Challenges/classic/Show.js rename to client/src/templates/Challenges/classic/Show.tsx index 44581370a9..85dc114b50 100644 --- a/client/src/templates/Challenges/classic/Show.js +++ b/client/src/templates/Challenges/classic/Show.tsx @@ -1,6 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// Package Utilities import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; +import { bindActionCreators, Dispatch } from 'redux'; import { createStructuredSelector } from 'reselect'; import { connect } from 'react-redux'; import Helmet from 'react-helmet'; @@ -8,6 +11,7 @@ import { graphql } from 'gatsby'; import Media from 'react-responsive'; import { withTranslation } from 'react-i18next'; +// Local Utilities import LearnLayout from '../../../components/layouts/Learn'; import MultifileEditor from './MultifileEditor'; import Preview from '../components/Preview'; @@ -20,12 +24,17 @@ import ResetModal from '../components/ResetModal'; import MobileLayout from './MobileLayout'; import DesktopLayout from './DesktopLayout'; import Hotkeys from '../components/Hotkeys'; - import { getGuideUrl } from '../utils'; import store from 'store'; import { challengeTypes } from '../../../../utils/challengeTypes'; import { isContained } from '../../../utils/is-contained'; -import { ChallengeNode } from '../../../redux/prop-types'; +import { + ChallengeNodeType, + ChallengeFilesType, + ChallengeMetaType, + TestType, + ResizePropsType +} from '../../../redux/prop-types'; import { createFiles, challengeFilesSelector, @@ -39,16 +48,18 @@ import { cancelTests } from '../redux'; +// Styles import './classic.css'; import '../components/test-frame.css'; +// Redux Setup const mapStateToProps = createStructuredSelector({ files: challengeFilesSelector, tests: challengeTestsSelector, output: consoleOutputSelector }); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( { createFiles, @@ -62,60 +73,58 @@ const mapDispatchToProps = dispatch => dispatch ); -const propTypes = { - cancelTests: PropTypes.func.isRequired, - challengeMounted: PropTypes.func.isRequired, - createFiles: PropTypes.func.isRequired, - data: PropTypes.shape({ - challengeNode: ChallengeNode - }), - executeChallenge: PropTypes.func.isRequired, - files: PropTypes.shape({ - key: PropTypes.string - }), - initConsole: PropTypes.func.isRequired, - initTests: PropTypes.func.isRequired, - output: PropTypes.arrayOf(PropTypes.string), - pageContext: PropTypes.shape({ - challengeMeta: PropTypes.shape({ - id: PropTypes.string, - nextChallengePath: PropTypes.string, - prevChallengePath: PropTypes.string - }) - }), - t: PropTypes.func.isRequired, - tests: PropTypes.arrayOf( - PropTypes.shape({ - text: PropTypes.string, - testString: PropTypes.string - }) - ), - updateChallengeMeta: PropTypes.func.isRequired -}; +// Types +interface ShowClassicProps { + cancelTests: () => void; + challengeMounted: (arg0: string) => void; + createFiles: (arg0: ChallengeFilesType) => void; + data: { challengeNode: ChallengeNodeType }; + executeChallenge: () => void; + files: ChallengeFilesType; + initConsole: (arg0: string) => void; + initTests: (tests: TestType[]) => void; + output: string[]; + pageContext: { + challengeMeta: ChallengeMetaType; + }; + t: (arg0: string) => string; + tests: TestType[]; + updateChallengeMeta: (arg0: ChallengeMetaType) => void; +} + +interface ShowClassicState { + resizing: boolean; +} + +interface IReflexLayout { + codePane: { flex: number }; + editorPane: { flex: number }; + instructionPane: { flex: number }; + previewPane: { flex: number }; + testsPane: { flex: number }; +} const MAX_MOBILE_WIDTH = 767; const REFLEX_LAYOUT = 'challenge-layout'; const BASE_LAYOUT = { - codePane: { - flex: 1 - }, - editorPane: { - flex: 1 - }, - instructionPane: { - flex: 1 - }, - previewPane: { - flex: 0.7 - }, - testsPane: { - flex: 0.25 - } + codePane: { flex: 1 }, + editorPane: { flex: 1 }, + instructionPane: { flex: 1 }, + previewPane: { flex: 0.7 }, + testsPane: { flex: 0.25 } }; -class ShowClassic extends Component { - constructor() { - super(); +// Component +class ShowClassic extends Component { + static displayName: string; + containerRef: React.RefObject; + editorRef: React.RefObject; + resizeProps: ResizePropsType; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + layoutState: any; + + constructor(props: ShowClassicProps) { + super(props); this.resizeProps = { onStopResize: this.onStopResize.bind(this), @@ -132,8 +141,8 @@ class ShowClassic extends Component { this.layoutState = this.getLayoutState(); } - getLayoutState() { - const reflexLayout = store.get(REFLEX_LAYOUT); + getLayoutState(): IReflexLayout | string { + const reflexLayout: IReflexLayout | string = store.get(REFLEX_LAYOUT); // Validate if user has not done any resize of the panes if (!reflexLayout) return BASE_LAYOUT; @@ -153,7 +162,8 @@ class ShowClassic extends Component { this.setState({ resizing: true }); } - onStopResize(event) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onStopResize(event: any) { const { name, flex } = event.component.props; this.setState({ resizing: false }); @@ -177,7 +187,7 @@ class ShowClassic extends Component { this.initializeComponent(title); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: ShowClassicProps) { const { data: { challengeNode: { @@ -199,7 +209,7 @@ class ShowClassic extends Component { } } - initializeComponent(title) { + initializeComponent(title: string) { const { challengeMounted, createFiles, @@ -256,7 +266,7 @@ class ShowClassic extends Component { ); } - renderInstructionsPanel({ showToolPanel }) { + renderInstructionsPanel({ showToolPanel }: { showToolPanel: boolean }) { const { block, description, instructions, superBlock, translationPending } = this.getChallenge(); @@ -397,7 +407,6 @@ class ShowClassic extends Component { } ShowClassic.displayName = 'ShowClassic'; -ShowClassic.propTypes = propTypes; export default connect( mapStateToProps, diff --git a/client/src/templates/Challenges/codeally/show.js b/client/src/templates/Challenges/codeally/show.tsx similarity index 74% rename from client/src/templates/Challenges/codeally/show.js rename to client/src/templates/Challenges/codeally/show.tsx index 47b685c504..73484e7ce3 100644 --- a/client/src/templates/Challenges/codeally/show.js +++ b/client/src/templates/Challenges/codeally/show.tsx @@ -2,20 +2,23 @@ // Package Utilities import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import Helmet from 'react-helmet'; import { graphql } from 'gatsby'; +import type { Dispatch } from 'redux'; // Local Utilities import LearnLayout from '../../../components/layouts/Learn'; -import { ChallengeNode } from '../../../redux/prop-types'; +import { + ChallengeNodeType, + ChallengeMetaType +} from '../../../redux/prop-types'; import { updateChallengeMeta, challengeMounted } from '../redux'; // Redux const mapStateToProps = () => ({}); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( { updateChallengeMeta, @@ -24,25 +27,19 @@ const mapDispatchToProps = dispatch => dispatch ); -// Proptypes -const propTypes = { - data: PropTypes.shape({ - challengeNode: ChallengeNode - }), - pageContext: PropTypes.shape({ - challengeMeta: PropTypes.shape({ - id: PropTypes.string, - introPath: PropTypes.string, - nextChallengePath: PropTypes.string, - prevChallengePath: PropTypes.string - }) - }), - updateChallengeMeta: PropTypes.func.isRequired -}; +// Types +interface ShowCodeAllyProps { + data: { challengeNode: ChallengeNodeType }; + pageContext: { + challengeMeta: ChallengeMetaType; + }; + updateChallengeMeta: (arg0: ChallengeMetaType) => void; +} // Component -class ShowCodeAlly extends Component { - componentDidMount() { +class ShowCodeAlly extends Component { + static displayName: string; + componentDidMount(): void { const { updateChallengeMeta, data: { @@ -65,6 +62,7 @@ class ShowCodeAlly extends Component {