diff --git a/client/package-lock.json b/client/package-lock.json index c0f90f22c0..15bfc69d26 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4074,11 +4074,6 @@ "resolved": "https://registry.npmjs.org/better-queue-memory/-/better-queue-memory-1.0.4.tgz", "integrity": "sha512-SWg5wFIShYffEmJpI6LgbL8/3Dqhku7xI1oEiy6FroP9DbcZlG0ZDjxvPdP9t7hTGW40IpIcC6zVoGT1oxjOuA==" }, - "bezier-easing": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", - "integrity": "sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY=" - }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", diff --git a/client/package.json b/client/package.json index e08641d2e9..da0041e8f4 100644 --- a/client/package.json +++ b/client/package.json @@ -18,7 +18,6 @@ "@reach/router": "^1.2.1", "algoliasearch": "^3.35.1", "axios": "^0.19.0", - "bezier-easing": "^2.1.0", "browser-cookies": "^1.2.0", "chai": "^4.2.0", "date-fns": "^1.30.1", diff --git a/client/src/templates/Challenges/classic/Show.js b/client/src/templates/Challenges/classic/Show.js index 18620dd494..6c8886ff8a 100644 --- a/client/src/templates/Challenges/classic/Show.js +++ b/client/src/templates/Challenges/classic/Show.js @@ -252,11 +252,7 @@ class ShowClassic extends Component { } render() { - const { - fields: { blockName }, - forumTopicId, - title - } = this.getChallenge(); + const { forumTopicId, title } = this.getChallenge(); const { executeChallenge, pageContext: { @@ -302,7 +298,7 @@ class ShowClassic extends Component { testOutput={this.renderTestOutput()} /> - + diff --git a/client/src/templates/Challenges/components/CompletionModal.js b/client/src/templates/Challenges/components/CompletionModal.js index 91d77ff5f4..c5794dcf10 100644 --- a/client/src/templates/Challenges/components/CompletionModal.js +++ b/client/src/templates/Challenges/components/CompletionModal.js @@ -4,11 +4,10 @@ import noop from 'lodash/noop'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; -import { useStaticQuery, graphql } from 'gatsby'; import ga from '../../../analytics'; import Login from '../../../components/Header/components/Login'; -import CompletionModalBody from './CompletionModalBody'; +import GreenPass from '../../../assets/icons/GreenPass'; import { dasherize } from '../../../../../utils/slugs'; @@ -17,7 +16,6 @@ import './completion-modal.css'; import { closeModal, submitChallenge, - completedChallengesIds, isCompletionModalOpenSelector, successMessageSelector, challengeFilesSelector, @@ -29,22 +27,12 @@ import { isSignedInSelector } from '../../../redux'; const mapStateToProps = createSelector( challengeFilesSelector, challengeMetaSelector, - completedChallengesIds, isCompletionModalOpenSelector, isSignedInSelector, successMessageSelector, - ( - files, - { title, id }, - completedChallengesIds, - isOpen, - isSignedIn, - message - ) => ({ + (files, { title }, isOpen, isSignedIn, message) => ({ files, title, - id, - completedChallengesIds, isOpen, isSignedIn, message @@ -71,13 +59,9 @@ const mapDispatchToProps = function(dispatch) { }; const propTypes = { - blockName: PropTypes.string, close: PropTypes.func.isRequired, - completedChallengesIds: PropTypes.array, - currentBlockIds: PropTypes.array, files: PropTypes.object.isRequired, handleKeypress: PropTypes.func.isRequired, - id: PropTypes.string, isOpen: PropTypes.bool, isSignedIn: PropTypes.bool.isRequired, message: PropTypes.string, @@ -85,27 +69,7 @@ const propTypes = { title: PropTypes.string }; -export function getCompletedPercent( - completedChallengesIds, - currentBlockIds, - currentChallengeId -) { - completedChallengesIds = completedChallengesIds.includes(currentChallengeId) - ? completedChallengesIds - : [...completedChallengesIds, currentChallengeId]; - - const completedChallengesInBlock = completedChallengesIds.filter(id => { - return currentBlockIds.includes(id); - }); - - const completedPercent = Math.round( - (completedChallengesInBlock.length / currentBlockIds.length) * 100 - ); - - return completedPercent > 100 ? 100 : completedPercent; -} - -export class CompletionModalInner extends Component { +export class CompletionModal extends Component { state = { downloadURL: null }; @@ -147,11 +111,7 @@ export class CompletionModalInner extends Component { render() { const { - blockName = '', close, - completedChallengesIds = [], - currentBlockIds = [], - id = '', isOpen, isSignedIn, submitChallenge, @@ -159,11 +119,6 @@ export class CompletionModalInner extends Component { message, title } = this.props; - - const completedPercent = !isSignedIn - ? 0 - : getCompletedPercent(completedChallengesIds, currentBlockIds, id); - if (isOpen) { ga.modalview('/completion-modal'); } @@ -182,13 +137,12 @@ export class CompletionModalInner extends Component { className='challenge-list-header fcc-modal' closeButton={true} > - {message} + {message} - + + + { - const { - allChallengeNode: { edges } - } = useStaticQuery(graphql` - query getCurrentBlockNodes { - allChallengeNode(sort: { fields: [superOrder, order, challengeOrder] }) { - edges { - node { - fields { - blockName - } - id - } - } - } - } - `); - - const currentBlockIds = edges - .filter(edge => edge.node.fields.blockName === blockName) - .map(edge => edge.node.id); - return currentBlockIds; -}; - -const CompletionModal = props => { - const currentBlockIds = useCurrentBlockIds(props.blockName || ''); - return ; -}; - CompletionModal.displayName = 'CompletionModal'; CompletionModal.propTypes = propTypes; diff --git a/client/src/templates/Challenges/components/CompletionModal.test.js b/client/src/templates/Challenges/components/CompletionModal.test.js deleted file mode 100644 index e10bc20ff3..0000000000 --- a/client/src/templates/Challenges/components/CompletionModal.test.js +++ /dev/null @@ -1,47 +0,0 @@ -/* global expect */ - -import '@testing-library/jest-dom/extend-expect'; - -import { getCompletedPercent } from './CompletionModal'; - -const completedChallengesIds = ['1', '3', '5'], - currentBlockIds = ['1', '3', '5', '7'], - id = '7', - fakeId = '12345', - fakeCompletedChallengesIds = ['1', '3', '5', '7', '8']; - -describe('', () => { - describe('getCompletedPercent', () => { - it('returns 0 if no challenges have been completed', () => { - expect(getCompletedPercent([], currentBlockIds, fakeId)).toBe(0); - }); - - it('returns 25 if one out of four challenges are complete', () => { - expect(getCompletedPercent([], currentBlockIds, currentBlockIds[1])).toBe( - 25 - ); - }); - - it('returns 75 if three out of four challenges are complete', () => { - expect( - getCompletedPercent( - completedChallengesIds, - currentBlockIds, - completedChallengesIds[0] - ) - ).toBe(75); - }); - - it('returns 100 if all challenges have been completed', () => { - expect( - getCompletedPercent(completedChallengesIds, currentBlockIds, id) - ).toBe(100); - }); - - it('returns 100 if more challenges have been complete than exist', () => { - expect( - getCompletedPercent(fakeCompletedChallengesIds, currentBlockIds, id) - ).toBe(100); - }); - }); -}); diff --git a/client/src/templates/Challenges/components/CompletionModalBody.js b/client/src/templates/Challenges/components/CompletionModalBody.js deleted file mode 100644 index 652eb5c6af..0000000000 --- a/client/src/templates/Challenges/components/CompletionModalBody.js +++ /dev/null @@ -1,97 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import BezierEasing from 'bezier-easing'; -import GreenPass from '../../../assets/icons/GreenPass'; - -const propTypes = { - blockName: PropTypes.string, - completedPercent: PropTypes.number -}; - -export class CompletionModalBody extends PureComponent { - constructor(props) { - super(props); - - this.state = { - progressInterval: null, - shownPercent: 0 - }; - - this.animateProgressBar = this.animateProgressBar.bind(this); - } - - animateProgressBar(completedPercent) { - const easing = BezierEasing(0.2, 0.5, 0.4, 1); - - if (completedPercent > 100) completedPercent = 100; - if (completedPercent < 0) completedPercent = 0; - - const transitionLength = completedPercent * 10 + 750; - const intervalLength = 10; - const intervalsToFinish = transitionLength / intervalLength; - const amountPerInterval = completedPercent / intervalsToFinish; - let percent = 0; - - const myInterval = setInterval(() => { - percent += amountPerInterval; - - if (percent > completedPercent) percent = completedPercent; - - this.setState({ - shownPercent: Math.round( - completedPercent * easing(percent / completedPercent) - ) - }); - - if (percent >= completedPercent) clearInterval(myInterval); - }, intervalLength); - - this.setState({ - progressInterval: myInterval - }); - } - - componentWillUnmount() { - clearInterval(this.state.progressInterval); - } - - render() { - const { blockName, completedPercent } = this.props; - - return ( - <> - - { - setTimeout(() => { - this.animateProgressBar(completedPercent); - }, 50); - }} - /> - - - {blockName} - - - {this.state.shownPercent}% complete - - - - {this.state.shownPercent}% complete - - - - - > - ); - } -} - -CompletionModalBody.displayName = 'CompletionModalBody'; -CompletionModalBody.propTypes = propTypes; - -export default CompletionModalBody; diff --git a/client/src/templates/Challenges/components/CompletionModalBody.test.js b/client/src/templates/Challenges/components/CompletionModalBody.test.js deleted file mode 100644 index 1159ab362d..0000000000 --- a/client/src/templates/Challenges/components/CompletionModalBody.test.js +++ /dev/null @@ -1,68 +0,0 @@ -/* global jest, expect */ - -import '@testing-library/jest-dom/extend-expect'; -import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; - -import CompletionModalBody from './CompletionModalBody'; - -const props = { - blockName: 'Basic HTML and HTML5', - completedPercent: Math.floor(Math.random() * 101) -}; - -describe('', () => { - test('matches snapshot', () => { - const { container } = render(); - - expect(container).toMatchSnapshot(); - }); - - describe('progress-bar', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - test('renders with 0% complete shown initially', () => { - const { getAllByText } = render(); - expect(getAllByText('0% complete').length).toBe(2); - }); - - test('renders with 0% width initially', () => { - const { container } = render(); - expect(container.querySelector('.progress-bar-percent')).toHaveAttribute( - 'style', - 'width: 0%;' - ); - }); - - test('shows the correct percent after animation', () => { - const { container, getAllByText } = render( - - ); - const progressBars = getAllByText('0% complete'); - fireEvent.animationEnd( - container.querySelector('.completion-success-icon') - ); - jest.runAllTimers(); - progressBars.forEach(bar => - expect(bar).toHaveTextContent(`${props.completedPercent}% complete`) - ); - }); - - test('has the correct width after animation', () => { - const { container } = render(); - - fireEvent.animationEnd( - container.querySelector('.completion-success-icon') - ); - - jest.runAllTimers(); - - expect(container.querySelector('.progress-bar-percent')).toHaveAttribute( - 'style', - `width: ${props.completedPercent}%;` - ); - }); - }); -}); diff --git a/client/src/templates/Challenges/components/__snapshots__/CompletionModalBody.test.js.snap b/client/src/templates/Challenges/components/__snapshots__/CompletionModalBody.test.js.snap deleted file mode 100644 index 86ef581518..0000000000 --- a/client/src/templates/Challenges/components/__snapshots__/CompletionModalBody.test.js.snap +++ /dev/null @@ -1,92 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` matches snapshot 1`] = ` - - - - Passed - - - - - Passed - - - - - - - - - - Basic HTML and HTML5 - - - - 0 - % complete - - - - 0 - % complete - - - - - -`; diff --git a/client/src/templates/Challenges/components/completion-modal.css b/client/src/templates/Challenges/components/completion-modal.css index cfd8e325f3..6f2a6563d6 100644 --- a/client/src/templates/Challenges/components/completion-modal.css +++ b/client/src/templates/Challenges/components/completion-modal.css @@ -1,119 +1,17 @@ -.completion-message { - text-align: center; - font-weight: 700; - font-size: 1.5rem; -} - .completion-modal-body { - min-height: 400px; + height: 45vh; display: flex; - flex-direction: column; - justify-content: space-evenly; -} - -.completion-challenge-details { - display: flex; - flex-direction: column; - align-items: center; justify-content: center; - margin-bottom: 15px; -} - -.completion-success-icon { - width: 200px; - height: 200px; - transform: scale(1.5); - opacity: 0; - animation: success-icon-animation 150ms linear 100ms forwards; -} - -@keyframes success-icon-animation { - 100% { - opacity: 1; - transform: scale(1); - } -} - -.completion-block-details { - display: flex; - flex-direction: column; align-items: center; - justify-content: space-between; } -.completion-block-name { - text-align: center; - font-weight: 700; - font-size: 1.2rem; - margin-bottom: 15px; +.success-icon-wrapper > svg { + height: 30vh; + width: 30vh; } -.progress-bar-wrap { - width: 400px; - height: 50px; - position: relative; -} - -.progress-bar-background { - width: 400px; - height: 50px; - color: var(--primary-color); - border: 3px solid var(--primary-color); - background-color: var(--quaternary-background); - display: flex; - align-items: center; - justify-content: center; - position: absolute; - top: 0; - left: 0; -} - -.progress-bar-percent { - width: 0; - overflow: hidden; - position: relative; - background-color: var(--primary-color); - transition: width 0ms linear; -} - -.progress-bar-foreground { - color: var(--primary-background); - width: 400px; - height: 50px; - display: flex; - align-items: center; - justify-content: center; -} - -@media screen and (max-width: 991px) { +@media screen and (max-width: 767px) { .challenge-success-modal .btn-lg { font-size: 16px; } - - .completion-modal-body { - min-height: 340px; - } - - .progress-bar-wrap, - .progress-bar-background, - .progress-bar-foreground { - width: 260px; - height: 40px; - } - - .completion-success-icon { - width: 160px; - height: 160px; - } - - .completion-message { - font-weight: 600; - font-size: 1.2rem; - } - - .completion-challenge-name, - .completion-block-name { - font-weight: 400; - font-size: 1rem; - } } diff --git a/client/src/templates/Challenges/projects/backend/Show.js b/client/src/templates/Challenges/projects/backend/Show.js index d62b93715f..8ca5625b80 100644 --- a/client/src/templates/Challenges/projects/backend/Show.js +++ b/client/src/templates/Challenges/projects/backend/Show.js @@ -236,7 +236,7 @@ export class BackEnd extends Component { - + diff --git a/client/src/templates/Challenges/projects/frontend/Show.js b/client/src/templates/Challenges/projects/frontend/Show.js index f13bad2d6a..3195b5ccbf 100644 --- a/client/src/templates/Challenges/projects/frontend/Show.js +++ b/client/src/templates/Challenges/projects/frontend/Show.js @@ -136,7 +136,7 @@ export class Project extends Component { - + diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index 7c12537257..dc075108f7 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -162,8 +162,6 @@ export const challengeFilesSelector = state => state[ns].challengeFiles; export const challengeMetaSelector = state => state[ns].challengeMeta; export const challengeTestsSelector = state => state[ns].challengeTests; export const consoleOutputSelector = state => state[ns].consoleOut; -export const completedChallengesIds = state => - completedChallengesSelector(state).map(node => node.id); export const isChallengeCompletedSelector = state => { const completedChallenges = completedChallengesSelector(state); const { id: currentChallengeId } = challengeMetaSelector(state);