diff --git a/client/src/templates/Challenges/classic/editor.tsx b/client/src/templates/Challenges/classic/editor.tsx index 8da35743fee..0534ded34b1 100644 --- a/client/src/templates/Challenges/classic/editor.tsx +++ b/client/src/templates/Challenges/classic/editor.tsx @@ -74,7 +74,7 @@ export interface EditorProps { canFocus: boolean; challengeFiles: ChallengeFiles; challengeType: number; - containerRef: MutableRefObject; + containerRef?: React.RefObject; description: string; dimensions?: Dimensions; editorRef: MutableRefObject; @@ -825,7 +825,7 @@ const Editor = (props: EditorProps): JSX.Element => { } function focusOnHotkeys() { - const currContainerRef = props.containerRef.current; + const currContainerRef = props.containerRef?.current; if (currContainerRef) { currContainerRef.focus(); } diff --git a/client/src/templates/Challenges/classic/multifile-editor.tsx b/client/src/templates/Challenges/classic/multifile-editor.tsx index 04a62f44bec..965c6006b46 100644 --- a/client/src/templates/Challenges/classic/multifile-editor.tsx +++ b/client/src/templates/Challenges/classic/multifile-editor.tsx @@ -42,6 +42,7 @@ type MultifileEditorProps = Pick< > & { visibleEditors: VisibleEditors; }; + const mapStateToProps = createSelector( visibleEditorsSelector, canFocusEditorSelector, diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index d0e0a9c3549..e831ff40bd8 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -211,7 +211,7 @@ function ShowClassic({ const { t } = useTranslation(); const [resizing, setResizing] = useState(false); const [usingKeyboardInTablist, setUsingKeyboardInTablist] = useState(false); - const containerRef = useRef(); + const containerRef = useRef(null); const editorRef = useRef(); const instructionsPanelRef = useRef(null); const isMobile = useMediaQuery({ @@ -415,12 +415,12 @@ function ShowClassic({ diff --git a/client/src/templates/Challenges/codeally/show.tsx b/client/src/templates/Challenges/codeally/show.tsx index 131e80030fe..49ec0dc0a7e 100644 --- a/client/src/templates/Challenges/codeally/show.tsx +++ b/client/src/templates/Challenges/codeally/show.tsx @@ -1,7 +1,7 @@ // Package Utilities import { Button } from '@freecodecamp/react-bootstrap'; import { graphql } from 'gatsby'; -import React, { Component, RefObject } from 'react'; +import React, { Component } from 'react'; import Helmet from 'react-helmet'; import type { TFunction } from 'i18next'; import { Trans, withTranslation } from 'react-i18next'; @@ -113,7 +113,7 @@ interface ShowCodeAllyProps { class ShowCodeAlly extends Component { static displayName: string; - private _container: RefObject | undefined; + private container: React.RefObject = React.createRef(); componentDidMount(): void { const { @@ -133,8 +133,7 @@ class ShowCodeAlly extends Component { helpCategory }); challengeMounted(challengeMeta.id); - - this._container?.current?.focus(); + this.container.current?.focus(); } componentWillUnmount() { @@ -237,7 +236,6 @@ class ShowCodeAlly extends Component { challenge => challenge.id === challengeId ); const titleContext = t('learn.github-link'); - return showCodeAlly ? ( @@ -252,7 +250,7 @@ class ShowCodeAlly extends Component { ) : ( diff --git a/client/src/templates/Challenges/components/hotkeys.tsx b/client/src/templates/Challenges/components/hotkeys.tsx index 4f97b914905..6bcfd22f536 100644 --- a/client/src/templates/Challenges/components/hotkeys.tsx +++ b/client/src/templates/Challenges/components/hotkeys.tsx @@ -1,9 +1,8 @@ import { navigate } from 'gatsby'; -import React, { MutableRefObject, RefObject } from 'react'; +import React from 'react'; import { HotKeys, GlobalHotKeys } from 'react-hotkeys'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { editor } from 'monaco-editor'; import type { ChallengeFiles, Test, @@ -25,6 +24,7 @@ import { } from '../redux/selectors'; import './hotkeys.css'; import { isFinalProject } from '../../../../../shared/config/challenge-types'; +import type { EditorProps } from '../classic/editor'; const mapStateToProps = createSelector( canFocusEditorSelector, @@ -61,24 +61,32 @@ const keyMap = { showShortcuts: 'shift+/' }; -interface HotkeysProps - extends Pick { - canFocusEditor: boolean; - challengeFiles: ChallengeFiles; - challengeType?: number; - children: React.ReactElement; - editorRef?: MutableRefObject; - executeChallenge?: (options?: { showCompletionModal: boolean }) => void; - submitChallenge: () => void; - innerRef: RefObject | undefined; - instructionsPanelRef?: React.RefObject; - setEditorFocusability: (arg0: boolean) => void; - setIsAdvancing: (arg0: boolean) => void; - tests: Test[]; - usesMultifileEditor?: boolean; - openShortcutsModal: () => void; - user: User; -} +export type HotkeysProps = Pick< + ChallengeMeta, + 'nextChallengePath' | 'prevChallengePath' +> & + Partial< + Pick< + EditorProps, + 'usesMultifileEditor' | 'editorRef' | 'challengeType' | 'executeChallenge' + > + > & + Pick< + EditorProps, + | 'containerRef' + | 'tests' + | 'challengeFiles' + | 'submitChallenge' + | 'setEditorFocusability' + > & { + canFocusEditor: boolean; + children: React.ReactElement; + instructionsPanelRef?: React.RefObject; + setEditorFocusability: (arg0: boolean) => void; + setIsAdvancing: (arg0: boolean) => void; + openShortcutsModal: () => void; + user: User; + }; function Hotkeys({ canFocusEditor, @@ -87,7 +95,7 @@ function Hotkeys({ instructionsPanelRef, editorRef, executeChallenge, - innerRef, + containerRef, nextChallengePath, prevChallengePath, setEditorFocusability, @@ -99,12 +107,12 @@ function Hotkeys({ user: { keyboardShortcuts } }: HotkeysProps): JSX.Element { const handlers = { - executeChallenge: (e?: KeyboardEvent) => { + executeChallenge: (keyEvent?: KeyboardEvent) => { // the 'enter' part of 'ctrl+enter' stops HotKeys from listening, so it // needs to be prevented. // TODO: 'enter' on its own also disables HotKeys, but default behaviour // should not be prevented in that case. - e?.preventDefault(); + keyEvent?.preventDefault(); if (!executeChallenge) return; @@ -126,8 +134,8 @@ function Hotkeys({ }, ...(keyboardShortcuts ? { - focusEditor: (e?: KeyboardEvent) => { - e?.preventDefault(); + focusEditor: (keyEvent?: KeyboardEvent) => { + keyEvent?.preventDefault(); if (editorRef && editorRef.current) { editorRef.current.focus(); } @@ -158,8 +166,8 @@ function Hotkeys({ } } }, - showShortcuts: (e?: KeyboardEvent) => { - if (!canFocusEditor && e?.shiftKey && e.key === '?') { + showShortcuts: (keyEvent?: KeyboardEvent) => { + if (!canFocusEditor && keyEvent?.shiftKey && keyEvent.key === '?') { openShortcutsModal(); } } @@ -177,7 +185,7 @@ function Hotkeys({ id='editor-layout' allowChanges={true} handlers={handlers} - innerRef={innerRef} + innerRef={containerRef} keyMap={keyMap} > {children} diff --git a/client/src/templates/Challenges/exam/show.tsx b/client/src/templates/Challenges/exam/show.tsx index 63e56cca1ed..4784021a9d3 100644 --- a/client/src/templates/Challenges/exam/show.tsx +++ b/client/src/templates/Challenges/exam/show.tsx @@ -138,7 +138,7 @@ function convertMd(md: string): string { class ShowExam extends Component { static displayName: string; - private _container: RefObject | undefined; + private container: RefObject | undefined = React.createRef(); timerInterval!: NodeJS.Timeout; constructor(props: ShowExamProps) { @@ -179,7 +179,7 @@ class ShowExam extends Component { }); challengeMounted(challengeMeta.id); - this._container?.current?.focus(); + this.container?.current?.focus(); } componentWillUnmount() { @@ -513,7 +513,7 @@ class ShowExam extends Component { ) : ( diff --git a/client/src/templates/Challenges/ms-trophy/show.tsx b/client/src/templates/Challenges/ms-trophy/show.tsx index 2ffc167a428..2987d71fa61 100644 --- a/client/src/templates/Challenges/ms-trophy/show.tsx +++ b/client/src/templates/Challenges/ms-trophy/show.tsx @@ -84,7 +84,7 @@ interface MsTrophyProps { // Component class MsTrophy extends Component { static displayName: string; - private _container: HTMLElement | null = null; + private container: React.RefObject = React.createRef(); constructor(props: MsTrophyProps) { super(props); @@ -108,7 +108,7 @@ class MsTrophy extends Component { helpCategory }); challengeMounted(challengeMeta.id); - this._container?.focus(); + this.container.current?.focus(); } componentDidUpdate(prevProps: MsTrophyProps): void { @@ -178,7 +178,7 @@ class MsTrophy extends Component { return ( (this._container = c)} + containerRef={this.container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} > diff --git a/client/src/templates/Challenges/odin/show.tsx b/client/src/templates/Challenges/odin/show.tsx index b096d6fdf77..37a90abe6eb 100644 --- a/client/src/templates/Challenges/odin/show.tsx +++ b/client/src/templates/Challenges/odin/show.tsx @@ -83,7 +83,7 @@ interface ShowOdinState { // Component class ShowOdin extends Component { static displayName: string; - private _container: HTMLElement | null | undefined; + private container: React.RefObject = React.createRef(); constructor(props: ShowOdinProps) { super(props); @@ -119,7 +119,7 @@ class ShowOdin extends Component { helpCategory }); challengeMounted(challengeMeta.id); - this._container?.focus(); + this.container.current?.focus(); } componentDidUpdate(prevProps: ShowOdinProps): void { @@ -236,7 +236,7 @@ class ShowOdin extends Component { executeChallenge={() => { this.handleSubmit(solution, openCompletionModal, assignments); }} - innerRef={(c: HTMLElement | null) => (this._container = c)} + containerRef={this.container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} > diff --git a/client/src/templates/Challenges/projects/backend/show.tsx b/client/src/templates/Challenges/projects/backend/show.tsx index aa056485193..bc6365a64a9 100644 --- a/client/src/templates/Challenges/projects/backend/show.tsx +++ b/client/src/templates/Challenges/projects/backend/show.tsx @@ -99,8 +99,8 @@ interface BackEndProps { // Component class BackEnd extends Component { static displayName: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _container: any; + private container: React.RefObject = React.createRef(); + constructor(props: BackEndProps) { super(props); this.state = {}; @@ -111,7 +111,7 @@ class BackEnd extends Component { componentDidMount() { this.initializeComponent(); window.addEventListener('resize', () => this.updateDimensions()); - this._container.focus(); + this.container.current?.focus(); } updateDimensions() { @@ -220,7 +220,7 @@ class BackEnd extends Component { return ( (this._container = c)} + containerRef={this.container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} > diff --git a/client/src/templates/Challenges/projects/frontend/show.tsx b/client/src/templates/Challenges/projects/frontend/show.tsx index 4a173c05255..071a82df952 100644 --- a/client/src/templates/Challenges/projects/frontend/show.tsx +++ b/client/src/templates/Challenges/projects/frontend/show.tsx @@ -64,7 +64,7 @@ interface ProjectProps { // Component class Project extends Component { static displayName: string; - private _container: HTMLElement | null = null; + private container: React.RefObject = React.createRef(); constructor(props: ProjectProps) { super(props); @@ -88,7 +88,7 @@ class Project extends Component { helpCategory }); challengeMounted(challengeMeta.id); - this._container?.focus(); + this.container.current?.focus(); } componentDidUpdate(prevProps: ProjectProps): void { @@ -161,7 +161,7 @@ class Project extends Component { return ( (this._container = c)} + containerRef={this.container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} > diff --git a/client/src/templates/Challenges/video/show.tsx b/client/src/templates/Challenges/video/show.tsx index 11261a97929..8348f54f84a 100644 --- a/client/src/templates/Challenges/video/show.tsx +++ b/client/src/templates/Challenges/video/show.tsx @@ -83,7 +83,7 @@ interface ShowVideoState { // Component class ShowVideo extends Component { static displayName: string; - private _container: HTMLElement | null | undefined; + private container: React.RefObject = React.createRef(); constructor(props: ShowVideoProps) { super(props); @@ -117,7 +117,7 @@ class ShowVideo extends Component { helpCategory }); challengeMounted(challengeMeta.id); - this._container?.focus(); + this.container.current?.focus(); } componentDidUpdate(prevProps: ShowVideoProps): void { @@ -213,7 +213,7 @@ class ShowVideo extends Component { executeChallenge={() => { this.handleSubmit(solution, openCompletionModal); }} - innerRef={(c: HTMLElement | null) => (this._container = c)} + containerRef={this.container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} >