Skip to content

PROD-2560 - fix issue where hints flash when you resubmit #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions client/src/templates/Challenges/classic/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ import {
stopResetting,
isProjectPreviewModalOpenSelector,
openModal,
isChallengeCompletedSelector
isChallengeCompletedSelector,
testsRunningSelector
} from '../redux';
import GreenPass from '../../../assets/icons/green-pass';
import Code from '../../../assets/icons/code';
Expand Down Expand Up @@ -107,6 +108,7 @@ interface EditorProps {
}) => void;
usesMultifileEditor: boolean;
isChallengeCompleted: boolean;
testsRunning: boolean;
}

// TODO: this is grab bag of unrelated properties. There's no need for them to
Expand Down Expand Up @@ -136,6 +138,7 @@ const mapStateToProps = createSelector(
userSelector,
challengeTestsSelector,
isChallengeCompletedSelector,
testsRunningSelector,
(
canFocus: boolean,
{ challengeType }: { challengeType: number },
Expand All @@ -146,7 +149,8 @@ const mapStateToProps = createSelector(
isSignedIn: boolean,
{ theme = Themes.Default }: { theme: Themes },
tests: [{ text: string; testString: string }],
isChallengeCompleted: boolean
isChallengeCompleted: boolean,
testsRunning: boolean
) => ({
canFocus: open ? false : canFocus,
challengeType,
Expand All @@ -156,7 +160,8 @@ const mapStateToProps = createSelector(
output,
theme,
tests,
isChallengeCompleted
isChallengeCompleted,
testsRunning
})
);

Expand Down Expand Up @@ -584,6 +589,7 @@ const Editor = (props: EditorProps): JSX.Element => {
challengeHasErrors={challengeHasErrors()}
tryToSubmitChallenge={tryToSubmitChallenge}
isEditorInFocus={isEditorInFocus}
isRunningTests={props.testsRunning}
/>,
outputNode,
callback
Expand Down Expand Up @@ -1089,7 +1095,7 @@ const Editor = (props: EditorProps): JSX.Element => {
dataRef.current.outputNode = lowerJawElement;
updateOutputZone();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.tests]);
}, [props.tests, props.testsRunning]);

useEffect(() => {
const editor = dataRef.current.editor;
Expand Down
97 changes: 51 additions & 46 deletions client/src/templates/Challenges/classic/lower-jaw.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useState, useEffect } from 'react';
import React, {
useState,
useEffect,
useMemo,
useCallback,
useRef
} from 'react';
import { useTranslation } from 'react-i18next';

import TestFail from '../../../assets/icons/test-fail';
Expand All @@ -16,18 +22,18 @@ interface LowerJawProps {
challengeHasErrors?: boolean;
testsLength?: number;
attemptsNumber?: number;
isRunningTests?: boolean;
}

const LowerJaw = ({
openHelpModal,
challengeIsCompleted,
challengeHasErrors,
hint,
tryToExecuteChallenge,
tryToSubmitChallenge,
attemptsNumber,
testsLength,
isEditorInFocus
isEditorInFocus,
isRunningTests
}: LowerJawProps): JSX.Element => {
const [previousHint, setpreviousHint] = useState('');
const [runningTests, setRunningTests] = useState(false);
Expand All @@ -37,6 +43,14 @@ const LowerJaw = ({
const { t } = useTranslation();
const submitButtonRef = React.createRef<HTMLButtonElement>();
const testFeedbackRef = React.createRef<HTMLDivElement>();
const challengeHasBeenCompletedRef = useRef(false);

// if a challenge was ever completed keep the state as completed
if (challengeIsCompleted) {
challengeHasBeenCompletedRef.current = true;
}
// keep the value of the reference.current as a separate value for convenience
const challengeHasBeenCompleted = challengeHasBeenCompletedRef.current;

useEffect(() => {
if (attemptsNumber && attemptsNumber > 0) {
Expand Down Expand Up @@ -67,15 +81,15 @@ const LowerJaw = ({
}, [challengeHasErrors, hint]);

useEffect(() => {
if (challengeIsCompleted && submitButtonRef?.current) {
if (challengeHasBeenCompleted && submitButtonRef?.current) {
submitButtonRef.current.focus();
setTimeout(() => {
setTestBtnariaHidden(true);
}, 500);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [challengeIsCompleted]);
}, [challengeHasBeenCompleted]);

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
Expand All @@ -84,12 +98,23 @@ const LowerJaw = ({
}
});

const renderTestFeedbackContainer = () => {
const sentencePicker = useCallback(() => {
const sentenceArray = [
'learn.sorry-try-again',
'learn.sorry-keep-trying',
'learn.sorry-getting-there',
'learn.sorry-hang-in-there',
'learn.sorry-dont-giveup'
];
return attemptsNumber
? sentenceArray[attemptsNumber % sentenceArray.length]
: sentenceArray[0];
}, [attemptsNumber]);

const feedbackContent = useMemo(() => {
if (attemptsNumber === 0) {
return '';
} else if (runningTests) {
return '';
} else if (challengeIsCompleted) {
} else if (challengeHasBeenCompleted) {
const submitKeyboardInstructions = isEditorInFocus ? (
<span className='sr-only'>{t('aria.submit')}</span>
) : (
Expand Down Expand Up @@ -142,69 +167,49 @@ const LowerJaw = ({
</>
);
}
};

const sentencePicker = () => {
const sentenceArray = [
'learn.sorry-try-again',
'learn.sorry-keep-trying',
'learn.sorry-getting-there',
'learn.sorry-hang-in-there',
'learn.sorry-dont-giveup'
];
return attemptsNumber
? sentenceArray[attemptsNumber % sentenceArray.length]
: sentenceArray[0];
};

const renderHelpButton = () => {
const isAtteptsLargerThanTest =
attemptsNumber && testsLength && attemptsNumber >= testsLength;

if (isAtteptsLargerThanTest && !challengeIsCompleted)
return (
<button
className='btn-block btn fade-in'
id='help-button'
onClick={openHelpModal}
>
{t('buttons.ask-for-help')}
</button>
);
};
}, [
attemptsNumber,
challengeHasBeenCompleted,
hint,
isEditorInFocus,
isFeedbackHidden,
previousHint,
sentencePicker,
t
]);

const renderButtons = () => {
return (
<>
<button
id='test-button'
className={`btn btn-primary ${challengeIsCompleted ? 'sr-only' : ''}`}
className={`btn btn-primary ${
challengeHasBeenCompleted ? 'sr-only' : ''
}`}
aria-hidden={testBtnariaHidden}
onClick={tryToExecuteChallenge}
>
{t('buttons.check-code')}
{isRunningTests && '...'}
</button>
<div id='action-buttons-container'>
<button
id='submit-button'
aria-hidden={!challengeIsCompleted}
aria-hidden={!challengeHasBeenCompleted}
className='btn btn-primary'
onClick={tryToSubmitChallenge}
ref={submitButtonRef}
>
{t('buttons.submit-and-go')}
</button>
{renderHelpButton()}
</div>
</>
);
};

const feedbackContent = renderTestFeedbackContainer();

return (
<div className='action-row-container'>
{feedbackContent && (
{!isRunningTests && feedbackContent && (
<div
style={runningTests ? { height: `${testFeedbackheight}px` } : {}}
className={`test-feedback`}
Expand Down
6 changes: 5 additions & 1 deletion client/src/templates/Challenges/classic/show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ import {
previewMounted,
updateChallengeMeta,
openModal,
setEditorFocusability
setEditorFocusability,
testsRunningSelector
} from '../redux';
import { savedChallengesSelector } from '../../../redux';
import { getGuideUrl } from '../utils';
Expand All @@ -61,6 +62,7 @@ import '../components/test-frame.css';
const mapStateToProps = createStructuredSelector({
challengeFiles: challengeFilesSelector,
tests: challengeTestsSelector,
testsRunning: testsRunningSelector,
output: consoleOutputSelector,
isChallengeCompleted: isChallengeCompletedSelector,
savedChallenges: savedChallengesSelector
Expand Down Expand Up @@ -104,6 +106,7 @@ interface ShowClassicProps {
};
t: TFunction;
tests: Test[];
testsRunning: boolean;
updateChallengeMeta: (arg0: ChallengeMeta) => void;
openModal: (modal: string) => void;
setEditorFocusability: (canFocus: boolean) => void;
Expand Down Expand Up @@ -337,6 +340,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
instructionsPanelRef={this.instructionsPanelRef}
showToolPanel={showToolPanel}
videoUrl={this.getVideoUrl()}
testsRunning={this.props.testsRunning}
/>
);
}
Expand Down
4 changes: 4 additions & 0 deletions client/src/templates/Challenges/components/side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface SidePanelProps {
instructionsPanelRef: React.RefObject<HTMLDivElement>;
showToolPanel: boolean;
tests: Test[];
testsRunning: boolean;
videoUrl: string;
}

Expand All @@ -38,6 +39,7 @@ export function SidePanel({
instructionsPanelRef,
showToolPanel = false,
tests,
testsRunning,
videoUrl
}: SidePanelProps): JSX.Element {
const isChallengeComplete = tests.every(test => test.pass && !test.err);
Expand Down Expand Up @@ -85,6 +87,7 @@ export function SidePanel({
guideUrl={guideUrl}
videoUrl={videoUrl}
challengeIsCompleted={isChallengeComplete}
isRunningTests={testsRunning}
/>
)}
<TestSuite tests={tests} />
Expand All @@ -93,6 +96,7 @@ export function SidePanel({
guideUrl={guideUrl}
videoUrl={videoUrl}
challengeIsCompleted={isChallengeComplete}
isRunningTests={testsRunning}
/>
)}
</div>
Expand Down
3 changes: 3 additions & 0 deletions client/src/templates/Challenges/components/tool-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface ToolPanelProps {
saveChallenge: () => void;
isMobile?: boolean;
isSignedIn: boolean;
isRunningTests?: boolean;
openHelpModal: () => void;
openVideoModal: () => void;
openResetModal: () => void;
Expand All @@ -66,6 +67,7 @@ function ToolPanel({
saveChallenge,
isMobile,
isSignedIn,
isRunningTests,
openHelpModal,
openVideoModal,
openResetModal,
Expand Down Expand Up @@ -95,6 +97,7 @@ function ToolPanel({
onClick={handleRunTests}
>
{isMobile ? t('buttons.run') : t('buttons.run-test')}
{isRunningTests && ' ...'}
</Button>
)}
{challengeIsCompleted && (
Expand Down
1 change: 1 addition & 0 deletions client/src/templates/Challenges/redux/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const actionTypes = createTypes(
'updateTests',
'updateLogs',
'cancelTests',
'updateTestsRunning',

'logsToConsole',

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ import {
updateTests,
openModal,
isBuildEnabledSelector,
disableBuildOnError
disableBuildOnError,
updateTestsRunning
} from './';

// How long before bailing out of a preview.
Expand Down Expand Up @@ -101,6 +102,7 @@ export function* executeChallengeSaga({ payload }) {
try {
yield put(initLogs());
yield put(initConsole(i18next.t('learn.running-tests')));
yield put(updateTestsRunning(true));
// reset tests to initial state
const tests = (yield select(challengeTestsSelector)).map(
({ text, testString }) => ({ text, testString })
Expand Down Expand Up @@ -139,6 +141,7 @@ export function* executeChallengeSaga({ payload }) {
}
yield put(updateConsole(i18next.t('learn.tests-completed')));
yield put(logsToConsole(i18next.t('learn.console-output')));
yield put(updateTestsRunning(false));
} catch (e) {
yield put(updateConsole(e));
} finally {
Expand Down
Loading