From d4e01b66fb5ce30f3c4c0877f473aa6ae619691f Mon Sep 17 00:00:00 2001 From: Niraj Nandish Date: Thu, 19 May 2022 19:44:22 +0400 Subject: [PATCH] chore: TS migrate `MultifileEditor` (#45857) --- client/src/declarations.d.ts | 1 + client/src/redux/prop-types.ts | 2 +- ...ultifileEditor.js => multifile-editor.tsx} | 95 +++++++++++-------- .../src/templates/Challenges/classic/show.tsx | 14 ++- 4 files changed, 65 insertions(+), 47 deletions(-) rename client/src/templates/Challenges/classic/{MultifileEditor.js => multifile-editor.tsx} (66%) diff --git a/client/src/declarations.d.ts b/client/src/declarations.d.ts index 8109b23f580..53d7d2fe927 100644 --- a/client/src/declarations.d.ts +++ b/client/src/declarations.d.ts @@ -1,4 +1,5 @@ declare module '@freecodecamp/react-bootstrap'; +declare module '@freecodecamp/loop-protect'; declare module '@freecodecamp/strip-comments'; declare module '@types/react-redux'; declare module '@types/validator'; diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index f8791d5df8d..f45f5ff31c1 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -346,7 +346,7 @@ export type CompletedChallenge = { }; export type Ext = 'js' | 'html' | 'css' | 'jsx'; -export type FileKey = 'scriptjs' | 'indexhtml' | 'stylescss'; +export type FileKey = 'scriptjs' | 'indexhtml' | 'stylescss' | 'indexjsx'; export type ChallengeMeta = { block: string; diff --git a/client/src/templates/Challenges/classic/MultifileEditor.js b/client/src/templates/Challenges/classic/multifile-editor.tsx similarity index 66% rename from client/src/templates/Challenges/classic/MultifileEditor.js rename to client/src/templates/Challenges/classic/multifile-editor.tsx index 3c84b66853a..0fc5ba87add 100644 --- a/client/src/templates/Challenges/classic/MultifileEditor.js +++ b/client/src/templates/Challenges/classic/multifile-editor.tsx @@ -1,8 +1,8 @@ -import PropTypes from 'prop-types'; -import React, { useRef } from 'react'; +import React, { MutableRefObject, RefObject, useRef } from 'react'; import { connect } from 'react-redux'; import { ReflexContainer, ReflexElement, ReflexSplitter } from 'react-reflex'; import { createSelector } from 'reselect'; +import { editor } from 'monaco-editor'; import { isDonationModalOpenSelector, userSelector } from '../../../redux'; import { canFocusEditorSelector, @@ -11,46 +11,58 @@ import { } from '../redux'; import { getTargetEditor } from '../utils/get-target-editor'; import './editor.css'; +import { + ChallengeFile, + Dimensions, + Ext, + FileKey, + ResizeProps, + Test +} from '../../../redux/prop-types'; +import { Themes } from '../../../components/settings/theme'; import Editor from './editor'; -const propTypes = { - canFocus: PropTypes.bool, - // TODO: use shape - challengeFiles: PropTypes.array, - containerRef: PropTypes.any.isRequired, - contents: PropTypes.string, - description: PropTypes.string, - dimensions: PropTypes.object, - editorRef: PropTypes.any.isRequired, - ext: PropTypes.string, - fileKey: PropTypes.string, - initialEditorContent: PropTypes.string, - initialExt: PropTypes.string, - initialTests: PropTypes.array, - output: PropTypes.arrayOf(PropTypes.string), - resizeProps: PropTypes.shape({ - onStopResize: PropTypes.func, - onResize: PropTypes.func - }), - theme: PropTypes.string, - title: PropTypes.string, - showProjectPreview: PropTypes.bool, - usesMultifileEditor: PropTypes.bool, - visibleEditors: PropTypes.shape({ - scriptjs: PropTypes.bool, - indexjsx: PropTypes.bool, - stylescss: PropTypes.bool, - indexhtml: PropTypes.bool - }) +type VisibleEditors = { + [key: string]: boolean; }; - +interface MultifileEditorProps { + canFocus?: boolean; + challengeFiles: ChallengeFile[]; + containerRef: RefObject; + contents?: string; + description: string; + dimensions?: Dimensions; + editorRef: MutableRefObject; + ext?: Ext; + fileKey?: string; + initialEditorContent?: string; + initialExt?: string; + initialTests: Test[]; + output?: string[]; + resizeProps: ResizeProps; + title: string; + showProjectPreview: boolean; + usesMultifileEditor: boolean; + visibleEditors: { + indexhtml?: boolean; + indexjsx?: boolean; + stylescss?: boolean; + scriptjs?: boolean; + }; +} const mapStateToProps = createSelector( visibleEditorsSelector, canFocusEditorSelector, consoleOutputSelector, isDonationModalOpenSelector, userSelector, - (visibleEditors, canFocus, output, open, { theme = 'default' }) => ({ + ( + visibleEditors: VisibleEditors, + canFocus: boolean, + output: string[], + open, + { theme = Themes.Default }: { theme: Themes } + ) => ({ visibleEditors, canFocus: open ? false : canFocus, output, @@ -58,21 +70,19 @@ const mapStateToProps = createSelector( }) ); -const MultifileEditor = props => { +const MultifileEditor = (props: MultifileEditorProps) => { const { challengeFiles, containerRef, description, editorRef, initialTests, - theme, resizeProps, title, visibleEditors: { stylescss, indexhtml, scriptjs, indexjsx }, usesMultifileEditor, showProjectPreview } = props; - const editorTheme = theme === 'night' ? 'vs-dark-custom' : 'vs-custom'; // TODO: the tabs mess up the rendering (scroll doesn't work properly and // the in-editor description) @@ -94,7 +104,7 @@ const MultifileEditor = props => { if (stylescss) editorKeys.push('stylescss'); if (scriptjs) editorKeys.push('scriptjs'); - const editorAndSplitterKeys = editorKeys.reduce((acc, key) => { + const editorAndSplitterKeys = editorKeys.reduce((acc: string[] | [], key) => { if (acc.length === 0) { return [key]; } else { @@ -124,12 +134,16 @@ const MultifileEditor = props => { canFocusOnMountRef={canFocusOnMountRef} challengeFiles={challengeFiles} containerRef={containerRef} - description={targetEditor === key ? description : null} + description={targetEditor === key ? description : ''} editorRef={editorRef} - fileKey={key} + fileKey={key as FileKey} initialTests={initialTests} resizeProps={resizeProps} - theme={editorTheme} + contents={props.contents ?? ''} + dimensions={props.dimensions ?? { height: 0, width: 0 }} + ext={props.ext ?? 'html'} + initialEditorContent={props.initialEditorContent ?? ''} + initialExt={props.initialExt ?? ''} title={title} usesMultifileEditor={usesMultifileEditor} showProjectPreview={showProjectPreview} @@ -145,6 +159,5 @@ const MultifileEditor = props => { }; MultifileEditor.displayName = 'MultifileEditor'; -MultifileEditor.propTypes = propTypes; export default connect(mapStateToProps)(MultifileEditor); diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index 91671cef925..a28208ef4b0 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -1,5 +1,5 @@ import { graphql } from 'gatsby'; -import React, { Component } from 'react'; +import React, { Component, MutableRefObject } from 'react'; import Helmet from 'react-helmet'; import { TFunction, withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; @@ -8,6 +8,7 @@ import Media from 'react-responsive'; import { bindActionCreators, Dispatch } from 'redux'; import { createStructuredSelector } from 'reselect'; import store from 'store'; +import { editor } from 'monaco-editor'; import { challengeTypes } from '../../../../utils/challenge-types'; import LearnLayout from '../../../components/layouts/learn'; @@ -51,7 +52,7 @@ import { } from '../redux'; import { savedChallengesSelector } from '../../../redux'; import { getGuideUrl } from '../utils'; -import MultifileEditor from './MultifileEditor'; +import MultifileEditor from './multifile-editor'; import DesktopLayout from './desktop-layout'; import MobileLayout from './mobile-layout'; @@ -141,7 +142,7 @@ const BASE_LAYOUT = { class ShowClassic extends Component { static displayName: string; containerRef: React.RefObject; - editorRef: React.RefObject; + editorRef: React.RefObject; instructionsPanelRef: React.RefObject; resizeProps: ResizeProps; @@ -385,7 +386,10 @@ class ShowClassic extends Component { challengeFiles={challengeFiles} containerRef={this.containerRef} description={description} - editorRef={this.editorRef} + // Try to remove unknown + editorRef={ + this.editorRef as MutableRefObject + } initialTests={tests} resizeProps={this.resizeProps} title={title} @@ -449,7 +453,7 @@ class ShowClassic extends Component { return ( } executeChallenge={executeChallenge} innerRef={this.containerRef} instructionsPanelRef={this.instructionsPanelRef}