mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-30 16:01:14 -04:00
feat: swap bootstrap progress bar with step complete one (#52011)
Co-authored-by: Ahmad Abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
1
client/src/components/Progress/index.tsx
Normal file
1
client/src/components/Progress/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './progress';
|
||||
14
client/src/components/Progress/progress-bar.tsx
Normal file
14
client/src/components/Progress/progress-bar.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
export function ProgressBar({ now }: { now: number }) {
|
||||
return (
|
||||
<>
|
||||
<div className='progress-bar-background'></div>
|
||||
<div
|
||||
className='progress-bar-percent'
|
||||
data-testid='fcc-progress-bar-percent'
|
||||
style={{ width: `${now}%` }}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||
|
||||
import BezierEasing from 'bezier-easing';
|
||||
import { ProgressBar } from './progress-bar';
|
||||
|
||||
interface ProgressBarInnerProps {
|
||||
interface ProgressInnerProps {
|
||||
completedPercent: number;
|
||||
title: string;
|
||||
meta: string;
|
||||
@@ -32,18 +33,17 @@ function useIsInViewport(ref: React.RefObject<HTMLDivElement>) {
|
||||
|
||||
return isIntersecting;
|
||||
}
|
||||
function ProgressBarInner({
|
||||
function ProgressInner({
|
||||
completedPercent,
|
||||
title,
|
||||
meta
|
||||
}: ProgressBarInnerProps): JSX.Element {
|
||||
}: ProgressInnerProps): JSX.Element {
|
||||
const [shownPercent, setShownPercent] = useState(0);
|
||||
const [progressBarInnerWidth, setProgressBarInnerWidth] = useState(0);
|
||||
const [lastShopwnPercent, setLastShownPercent] = useState(0);
|
||||
const progressBarInnerWrap = useRef<HTMLDivElement>(null);
|
||||
const isProgressBarInViewport = useIsInViewport(progressBarInnerWrap);
|
||||
const progressInnerWrap = useRef<HTMLDivElement>(null);
|
||||
const isProgressInViewport = useIsInViewport(progressInnerWrap);
|
||||
|
||||
const animateProgressBarInner = (completedPercent: number) => {
|
||||
const animateProgressInner = (completedPercent: number) => {
|
||||
if (completedPercent > 100) completedPercent = 100;
|
||||
if (completedPercent < 0) completedPercent = 0;
|
||||
|
||||
@@ -66,17 +66,12 @@ function ProgressBarInner({
|
||||
}, intervalLength);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (lastShopwnPercent !== completedPercent && isProgressBarInViewport) {
|
||||
if (lastShopwnPercent !== completedPercent && isProgressInViewport) {
|
||||
setLastShownPercent(completedPercent);
|
||||
animateProgressBarInner(completedPercent);
|
||||
animateProgressInner(completedPercent);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isProgressBarInViewport]);
|
||||
|
||||
useEffect(() => {
|
||||
if (progressBarInnerWrap.current)
|
||||
setProgressBarInnerWidth(progressBarInnerWrap.current.offsetWidth);
|
||||
}, [progressBarInnerWrap]);
|
||||
}, [isProgressInViewport]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -84,25 +79,15 @@ function ProgressBarInner({
|
||||
<div
|
||||
className='progress-bar-wrap'
|
||||
aria-hidden='true'
|
||||
ref={progressBarInnerWrap}
|
||||
ref={progressInnerWrap}
|
||||
>
|
||||
<div className='progress-bar-background'></div>
|
||||
<div
|
||||
className='progress-bar-percent'
|
||||
data-testid='fcc-progress-bar-percent'
|
||||
style={{ width: `${shownPercent}%` }}
|
||||
>
|
||||
<div
|
||||
className='progress-bar-foreground'
|
||||
style={{ width: progressBarInnerWidth }}
|
||||
></div>
|
||||
</div>
|
||||
<ProgressBar now={shownPercent} />
|
||||
</div>
|
||||
<div className='completion-block-meta'>{meta}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ProgressBarInner.displayName = 'ProgressBarInner';
|
||||
ProgressInner.displayName = 'ProgressInner';
|
||||
|
||||
export default ProgressBarInner;
|
||||
export default ProgressInner;
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
completedPercentageSelector
|
||||
} from '../../templates/Challenges/redux/selectors';
|
||||
import { liveCerts } from '../../../config/cert-and-project-map';
|
||||
import ProgressBarInner from './progress-bar-inner';
|
||||
import ProgressInner from './progress-inner';
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
currentBlockIdsSelector,
|
||||
@@ -42,10 +42,10 @@ const mapStateToProps = createSelector(
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
|
||||
interface ProgressBarProps extends StateProps {
|
||||
interface ProgressProps extends StateProps {
|
||||
t: TFunction;
|
||||
}
|
||||
function ProgressBar({
|
||||
function Progress({
|
||||
currentBlockIds,
|
||||
block,
|
||||
id,
|
||||
@@ -53,7 +53,7 @@ function ProgressBar({
|
||||
completedChallengesInBlock,
|
||||
completedPercent,
|
||||
t
|
||||
}: ProgressBarProps): JSX.Element {
|
||||
}: ProgressProps): JSX.Element {
|
||||
const blockTitle = t(`intro:${superBlock}.blocks.${block}.title`);
|
||||
// Always false for legacy full stack, since it has no projects.
|
||||
const isCertificationProject = liveCerts.some(
|
||||
@@ -75,7 +75,7 @@ function ProgressBar({
|
||||
className='progress-bar-container'
|
||||
data-playwright-test-label='progress-bar-container'
|
||||
>
|
||||
<ProgressBarInner
|
||||
<ProgressInner
|
||||
title={blockTitle}
|
||||
meta={meta}
|
||||
completedPercent={completedPercent}
|
||||
@@ -84,6 +84,6 @@ function ProgressBar({
|
||||
);
|
||||
}
|
||||
|
||||
ProgressBar.displayName = 'ProgressBar';
|
||||
Progress.displayName = 'Progress';
|
||||
|
||||
export default connect(mapStateToProps)(withTranslation()(ProgressBar));
|
||||
export default connect(mapStateToProps)(withTranslation()(Progress));
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './progress-bar';
|
||||
@@ -15,7 +15,7 @@ import { apiLocation } from '../../../../config/env.json';
|
||||
import { ChallengeMeta } from '../../../redux/prop-types';
|
||||
import { Share } from '../../../components/share';
|
||||
import { ShareProps } from '../../../components/share/types';
|
||||
import ProgressBar from '../../../components/ProgressBar';
|
||||
import Progress from '../../../components/Progress';
|
||||
import Quote from '../../../assets/icons/quote';
|
||||
import {
|
||||
challengeMetaSelector,
|
||||
@@ -370,7 +370,7 @@ const LowerJaw = ({
|
||||
<>
|
||||
<hr></hr>
|
||||
<div className='progress-bar-container'>
|
||||
<ProgressBar />
|
||||
<Progress />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -97,22 +97,13 @@
|
||||
}
|
||||
|
||||
.progress-bar-percent {
|
||||
width: 0;
|
||||
height: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background-color: var(--primary-color);
|
||||
transition: width 0ms linear;
|
||||
}
|
||||
|
||||
.progress-bar-foreground {
|
||||
color: var(--primary-background);
|
||||
width: 400px;
|
||||
height: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
.challenge-success-modal .btn-lg {
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -18,7 +18,7 @@ import CompletionModal from './completion-modal';
|
||||
|
||||
jest.mock('../../../analytics');
|
||||
jest.mock('../../../utils/fire-confetti');
|
||||
jest.mock('../../../components/ProgressBar');
|
||||
jest.mock('../../../components/Progress');
|
||||
jest.mock('../redux/selectors');
|
||||
jest.mock('../utils/build');
|
||||
const mockFireConfetti = fireConfetti as jest.Mock;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
challengeMetaSelector,
|
||||
isSubmittingSelector
|
||||
} from '../redux/selectors';
|
||||
import ProgressBar from '../../../components/ProgressBar';
|
||||
import Progress from '../../../components/Progress';
|
||||
import GreenPass from '../../../assets/icons/green-pass';
|
||||
|
||||
import './completion-modal.css';
|
||||
@@ -189,7 +189,7 @@ class CompletionModal extends Component<
|
||||
/>
|
||||
</div>
|
||||
<div className='completion-block-details'>
|
||||
<ProgressBar />
|
||||
<Progress />
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import type { DefaultTFuncReturn, TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { ProgressBar } from '@freecodecamp/react-bootstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import ScrollableAnchor from 'react-scrollable-anchor';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { SuperBlocks } from '../../../../../shared/config/superblocks';
|
||||
import envData from '../../../../config/env.json';
|
||||
import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited';
|
||||
@@ -13,6 +13,7 @@ import Caret from '../../../assets/icons/caret';
|
||||
import DropDown from '../../../assets/icons/dropdown';
|
||||
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
|
||||
import GreenPass from '../../../assets/icons/green-pass';
|
||||
import { ProgressBar } from '../../../components/Progress/progress-bar';
|
||||
import { Link, Spacer } from '../../../components/helpers';
|
||||
import { completedChallengesSelector } from '../../../redux/selectors';
|
||||
import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types';
|
||||
@@ -153,7 +154,9 @@ class Block extends Component<BlockProps> {
|
||||
|
||||
const progressBarRender = (
|
||||
<div aria-hidden='true' className='progress-wrapper'>
|
||||
<ProgressBar now={percentageCompleted} />
|
||||
<div>
|
||||
<ProgressBar now={percentageCompleted} />
|
||||
</div>
|
||||
<span>{`${percentageCompleted}%`}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -443,8 +443,10 @@ ToDo: find out why, and remove the need for it */
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.block-grid .progress-wrapper span {
|
||||
color: var(--quaternary-color);
|
||||
.block-grid .progress-wrapper > div {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
padding-inline-end: 0.25em;
|
||||
}
|
||||
|
||||
.block-grid .progress {
|
||||
|
||||
Reference in New Issue
Block a user