mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-01-07 00:03:44 -05:00
feat: add progress bar to lower jaw (#50167)
Co-authored-by: IsmailTlemcani <ismail.tlemcani@gmail.com> Co-authored-by: ahmad abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||
|
||||
import BezierEasing from 'bezier-easing';
|
||||
|
||||
interface ProgressBarInnerProps {
|
||||
@@ -10,17 +11,37 @@ interface ProgressBarInnerProps {
|
||||
const easing = BezierEasing(0.2, 0.5, 0.4, 1);
|
||||
const intervalLength = 10;
|
||||
let percent = 0;
|
||||
let applyAnimation = true;
|
||||
|
||||
function useIsInViewport(ref: React.RefObject<HTMLDivElement>) {
|
||||
const [isIntersecting, setIsIntersecting] = useState(false);
|
||||
|
||||
const observer = useMemo(
|
||||
() =>
|
||||
new IntersectionObserver(([entry]) =>
|
||||
setIsIntersecting(entry.isIntersecting)
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current && observer.observe(ref.current);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [ref, observer]);
|
||||
|
||||
return isIntersecting;
|
||||
}
|
||||
function ProgressBarInner({
|
||||
completedPercent,
|
||||
title,
|
||||
meta
|
||||
}: ProgressBarInnerProps): JSX.Element {
|
||||
const [shownPercent, setShownPercent] = useState(0);
|
||||
const [progressInterval, setProgressInterval] = useState(0);
|
||||
const [progressBarInnerWidth, setProgressBarInnerWidth] = useState(0);
|
||||
const [lastShopwnPercent, setLastShownPercent] = useState(0);
|
||||
const progressBarInnerWrap = useRef<HTMLDivElement>(null);
|
||||
const isProgressBarInViewport = useIsInViewport(progressBarInnerWrap);
|
||||
|
||||
const animateProgressBarInner = (completedPercent: number) => {
|
||||
if (completedPercent > 100) completedPercent = 100;
|
||||
@@ -38,21 +59,19 @@ function ProgressBarInner({
|
||||
setShownPercent(
|
||||
Math.round(completedPercent * easing(percent / completedPercent))
|
||||
);
|
||||
if (percent >= completedPercent) clearInterval(myInterval);
|
||||
if (percent >= completedPercent) {
|
||||
percent = 0;
|
||||
clearInterval(myInterval);
|
||||
}
|
||||
}, intervalLength);
|
||||
|
||||
setProgressInterval(myInterval);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (applyAnimation) animateProgressBarInner(completedPercent);
|
||||
return () => {
|
||||
if (progressInterval !== null) {
|
||||
clearInterval(progressInterval);
|
||||
applyAnimation = false;
|
||||
}
|
||||
};
|
||||
if (lastShopwnPercent !== completedPercent && isProgressBarInViewport) {
|
||||
setLastShownPercent(completedPercent);
|
||||
animateProgressBarInner(completedPercent);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [completedPercent]);
|
||||
}, [isProgressBarInViewport]);
|
||||
|
||||
useEffect(() => {
|
||||
if (progressBarInnerWrap.current)
|
||||
|
||||
@@ -9,7 +9,7 @@ import Help from '../../../assets/icons/help';
|
||||
import Reset from '../../../assets/icons/reset';
|
||||
import { MAX_MOBILE_WIDTH } from '../../../../../config/misc';
|
||||
import { apiLocation } from '../../../../../config/env.json';
|
||||
|
||||
import ProgressBar from '../../../components/ProgressBar';
|
||||
const lowerJawButtonStyle = 'btn-block btn';
|
||||
|
||||
interface LowerJawPanelProps {
|
||||
@@ -310,6 +310,14 @@ const LowerJaw = ({
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{challengeIsCompleted && (
|
||||
<>
|
||||
<hr></hr>
|
||||
<div className='progress-bar-container'>
|
||||
<ProgressBar />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<LowerButtonsPanel
|
||||
resetButtonText={t('buttons.reset')}
|
||||
helpButtonText={t('buttons.help')}
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('progress bar', () => {
|
||||
});
|
||||
|
||||
it(
|
||||
'Should show the progress bar showing the completed percent',
|
||||
'Should show the progress bar showing the completed percent on legacy challenges',
|
||||
{ browser: 'electron' },
|
||||
() => {
|
||||
cy.visit(
|
||||
@@ -22,4 +22,64 @@ describe('progress bar', () => {
|
||||
cy.get('.progress-bar-container').contains('1% complete');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'Should show the progress bar showing the completed percent on modern challenges',
|
||||
{ browser: 'electron' },
|
||||
() => {
|
||||
cy.visit(
|
||||
'/learn/2022/responsive-web-design/learn-html-by-building-a-cat-photo-app/step-2'
|
||||
);
|
||||
cy.get(`${'.react-monaco-editor-container'} textarea`, { timeout: 16000 })
|
||||
.click()
|
||||
.focused()
|
||||
.type('{ctrl}a')
|
||||
.clear()
|
||||
.type(`<h1>CatPhotoApp</h1>\n<h2>Cat Photos</h2>`);
|
||||
cy.contains('Check Your Code (Ctrl + Enter)').click({ force: true });
|
||||
cy.contains('Submit and go to next challenge');
|
||||
cy.get('.progress-bar-container').contains('1% complete');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'Should show the progress bar showing the completed percent on modern challenges',
|
||||
{ browser: 'electron' },
|
||||
() => {
|
||||
cy.visit(
|
||||
'/learn/2022/responsive-web-design/learn-html-by-building-a-cat-photo-app/step-3'
|
||||
);
|
||||
cy.get(`${'.react-monaco-editor-container'} textarea`, { timeout: 16000 })
|
||||
.click()
|
||||
.focused()
|
||||
.type('{ctrl}a')
|
||||
.clear()
|
||||
.type(
|
||||
`<h2>Cat Photos</h2>\n<p>See more cat photos in our gallery.</p>`
|
||||
);
|
||||
cy.contains('Check Your Code (Ctrl + Enter)').click({ force: true });
|
||||
cy.contains('Submit and go to next challenge');
|
||||
cy.get('.progress-bar-container').contains('1% complete');
|
||||
}
|
||||
);
|
||||
it(
|
||||
'Should show the progress bar showing the completed percent on modern challenges',
|
||||
{ browser: 'electron' },
|
||||
() => {
|
||||
cy.visit(
|
||||
'/learn/2022/responsive-web-design/learn-html-by-building-a-cat-photo-app/step-4'
|
||||
);
|
||||
cy.get(`${'.react-monaco-editor-container'} textarea`, { timeout: 16000 })
|
||||
.click()
|
||||
.focused()
|
||||
.type('{ctrl}a')
|
||||
.clear()
|
||||
.type(
|
||||
`<!-- TODO: Add link to cat photos -->\n<p>See more cat photos in our gallery.</p>`
|
||||
);
|
||||
cy.contains('Check Your Code (Ctrl + Enter)').click({ force: true });
|
||||
cy.contains('Submit and go to next challenge');
|
||||
cy.get('.progress-bar-container').contains('3% complete');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user