diff --git a/client/package-lock.json b/client/package-lock.json index 4a2ab5dbaf..9675f6ecc5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -7310,6 +7310,11 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "eventlistener": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz", + "integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg=" + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -17398,6 +17403,17 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lazy-load": { + "version": "3.1.13", + "resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-3.1.13.tgz", + "integrity": "sha512-eAVNUn3vhNj79Iv04NOCwy/sCLyqDEhL3j9aJKV7VJuRBDg6rCiB+BIWHuG7VXJGCgb//6nX/soR8PTyWRhFvQ==", + "requires": { + "eventlistener": "0.0.1", + "lodash.debounce": "^4.0.0", + "lodash.throttle": "^4.0.0", + "prop-types": "^15.5.8" + } + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", diff --git a/client/package.json b/client/package.json index f8c41d8771..bfae9c48e0 100644 --- a/client/package.json +++ b/client/package.json @@ -56,6 +56,7 @@ "react-hotkeys": "^2.0.0", "react-identicons": "^1.1.7", "react-instantsearch-dom": "^6.0.0-beta.0", + "react-lazy-load": "^3.1.13", "react-monaco-editor": "^0.31.0", "react-redux": "^5.0.7", "react-reflex": "^3.0.18", diff --git a/client/src/assets/icons/LinkButton.js b/client/src/assets/icons/LinkButton.js new file mode 100644 index 0000000000..2501f384d7 --- /dev/null +++ b/client/src/assets/icons/LinkButton.js @@ -0,0 +1,34 @@ +import React from 'react'; + +export default function LinkButton(props) { + return ( + + ); +} diff --git a/client/src/assets/images/AmazonLogo.js b/client/src/assets/images/components/AmazonLogo.js similarity index 99% rename from client/src/assets/images/AmazonLogo.js rename to client/src/assets/images/components/AmazonLogo.js index a2cfdc4915..826559f202 100644 --- a/client/src/assets/images/AmazonLogo.js +++ b/client/src/assets/images/components/AmazonLogo.js @@ -7,7 +7,7 @@ function SpotifyLogo(props) { return (

+

{name ? `Welcome back, ${name}.` : `Welcome to freeCodeCamp.org`}

@@ -101,9 +101,7 @@ function Intro({ -

- Welcome to freeCodeCamp's curriculum. -

+

Welcome to freeCodeCamp's curriculum.

diff --git a/client/src/components/Intro/intro.css b/client/src/components/Intro/intro.css index 4868c53ff1..01d9a564df 100644 --- a/client/src/components/Intro/intro.css +++ b/client/src/components/Intro/intro.css @@ -9,14 +9,6 @@ white-space: pre-line; } -.logo-row { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-around; - align-content: center; -} - .logo-row h2 { height: 35px; padding: 0 10px 0 10px; @@ -27,11 +19,6 @@ padding-left: 0px; } -#learn-app-wrapper h2.medium-heading { - margin-bottom: 50px; - text-align: center; -} - .quote-partial .blockquote { font-size: 1.3rem; border: none; diff --git a/client/src/components/helpers/CurrentChallengeLink.js b/client/src/components/helpers/CurrentChallengeLink.js index c4c2bb6f66..bb149f1b95 100644 --- a/client/src/components/helpers/CurrentChallengeLink.js +++ b/client/src/components/helpers/CurrentChallengeLink.js @@ -15,7 +15,7 @@ function CurrentChallengeLink({ children, isLargeBtn }) { if (isLargeBtn) { classNames = 'btn btn-lg btn-primary btn-block'; } else { - classNames = 'btn btn-cta-big btn-primary btn-block'; + classNames = 'btn btn-primary btn-block'; } return ( diff --git a/client/src/components/helpers/ImageLoader.js b/client/src/components/helpers/ImageLoader.js new file mode 100644 index 0000000000..7a4342ef9d --- /dev/null +++ b/client/src/components/helpers/ImageLoader.js @@ -0,0 +1,65 @@ +import React from 'react'; + +import './image-loader.css'; +import LazyLoad from 'react-lazy-load'; +import PropTypes from 'prop-types'; + +const propTypes = { + alt: PropTypes.string, + className: PropTypes.string, + height: PropTypes.number, + loadedClassName: PropTypes.string, + loadingClassName: PropTypes.string, + src: PropTypes.string, + width: PropTypes.number +}; + +class ImageLoader extends React.Component { + // initial state: image loaded stage + state = { + loaded: false + }; + + // define our loading and loaded image classes + static defaultProps = { + className: '', + loadingClassName: 'img-loading', + loadedClassName: 'img-loaded' + }; + + // image onLoad handler to update state to loaded + onLoad = () => { + this.setState(() => ({ loaded: true })); + }; + + render() { + let { + className, + loadedClassName, + loadingClassName, + alt, + src, + width, + height + } = this.props; + + className = `${className} ${ + this.state.loaded ? loadedClassName : loadingClassName + }`; + + return ( + + {/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */} + {alt} + {/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */} + + ); + } +} +ImageLoader.propTypes = propTypes; +export default ImageLoader; diff --git a/client/src/components/helpers/image-loader.css b/client/src/components/helpers/image-loader.css new file mode 100644 index 0000000000..127e71b8b1 --- /dev/null +++ b/client/src/components/helpers/image-loader.css @@ -0,0 +1,23 @@ +@keyframes fadeInImg { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.img-loading { + opacity: 0; + width: 100%; + height: auto; +} + +.img-loaded { + animation: fadeInImg cubic-bezier(0.23, 1, 0.32, 1) 1; + position: relative; + opacity: 0; + animation-fill-mode: forwards; + animation-duration: 0.7s; + animation-delay: 0.1s; +} diff --git a/client/src/components/helpers/index.js b/client/src/components/helpers/index.js index da9b95ca94..ac6353802b 100644 --- a/client/src/components/helpers/index.js +++ b/client/src/components/helpers/index.js @@ -6,3 +6,4 @@ export { default as SkeletonSprite } from './SkeletonSprite'; export { default as Spacer } from './Spacer'; export { default as Link } from './Link'; export { default as CurrentChallengeLink } from './CurrentChallengeLink'; +export { default as ImageLoader } from './ImageLoader'; diff --git a/client/src/components/landing/components/AsSeenIn.js b/client/src/components/landing/components/AsSeenIn.js new file mode 100644 index 0000000000..36db0e5e45 --- /dev/null +++ b/client/src/components/landing/components/AsSeenIn.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { Col, Row } from '@freecodecamp/react-bootstrap'; + +import { AsFeatureLogo } from '../../../assets/images/components'; + +const AsSeenIn = () => ( + + +
+

As seen in:

+ +
+ +
+); + +AsSeenIn.displayName = 'AsSeenIn'; +export default AsSeenIn; diff --git a/client/src/components/landing/components/BigCallToAction.js b/client/src/components/landing/components/BigCallToAction.js new file mode 100644 index 0000000000..b233fa8931 --- /dev/null +++ b/client/src/components/landing/components/BigCallToAction.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Login from '../../Header/components/Login'; + +const propTypes = { + page: PropTypes.string +}; + +const BigCallToAction = ({ page }) => ( + + {page === 'landing' + ? "Get started (it's free)" + : "Sign in to save your progress (it's free)"} + +); + +BigCallToAction.displayName = 'BigCallToAction'; +BigCallToAction.propTypes = propTypes; +export default BigCallToAction; diff --git a/client/src/components/landing/components/Certifications.js b/client/src/components/landing/components/Certifications.js new file mode 100644 index 0000000000..eaf154af30 --- /dev/null +++ b/client/src/components/landing/components/Certifications.js @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Col, Row } from '@freecodecamp/react-bootstrap'; +import { uniq } from 'lodash'; +import { Spacer, Link } from '../../helpers'; +import LinkButton from '../../../assets/icons/LinkButton'; +import BigCallToAction from './BigCallToAction'; + +const propTypes = { + nodes: PropTypes.array, + page: PropTypes.string +}; + +const Certifications = ({ nodes, page }) => { + const superBlocks = uniq(nodes.map(node => node.superBlock)).filter( + cert => cert !== 'Coding Interview Prep' + ); + + return ( + + +

Earn free verified certifications in:

+
    + {superBlocks.map((superBlock, i) => ( +
  • + + {superBlock} + + +
  • + ))} +
+ + + + +
+ ); +}; + +Certifications.displayName = 'Certifications'; +Certifications.propTypes = propTypes; +export default Certifications; diff --git a/client/src/components/landing/components/CompanyLogos.js b/client/src/components/landing/components/CompanyLogos.js deleted file mode 100644 index 64ace2dd66..0000000000 --- a/client/src/components/landing/components/CompanyLogos.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { - AmazonLogo, - AppleLogo, - MicrosoftLogo, - SpotifyLogo, - GoogleLogo -} from '../../../assets/images'; - -import './companyLogos.css'; - -export const CompanyLogos = () => { - return ( -
- - - - - -
- ); -}; - -CompanyLogos.displayName = 'CompanyLogos'; -export default CompanyLogos; diff --git a/client/src/components/landing/components/LandingTop.js b/client/src/components/landing/components/LandingTop.js new file mode 100644 index 0000000000..12ef44a9c7 --- /dev/null +++ b/client/src/components/landing/components/LandingTop.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Media from 'react-responsive'; +import { Col, Row } from '@freecodecamp/react-bootstrap'; +import { Spacer, ImageLoader } from '../../helpers'; +import wideImg from '../../../assets/images/landing/wide-image.png'; +import Login from '../../Header/components/Login'; +import { + AmazonLogo, + AppleLogo, + MicrosoftLogo, + SpotifyLogo, + GoogleLogo +} from '../../../assets/images/components'; + +const propTypes = { + page: PropTypes.string +}; + +const LARGE_SCREEN_SIZE = 1200; + +function landingTop({ page }) { + const landingImageSection = ( + + + +

+ freeCodeCamp students at a local study group in South Korea. +

+
+ ); + const BigCallToAction = ( + + {page === 'landing' + ? "Get started (it's free)" + : "Sign in to save your progress (it's free)"} + + ); + return ( +
+ + + +

+ Learn to code at home. +

+

Build projects.

+

Earn certifications.

+

+ Since 2014, more than 40,000 freeCodeCamp.org graduates have gotten + jobs at tech companies including: +

+
+ + + + + +
+ + {BigCallToAction} + {landingImageSection} + + +
+
+ ); +} + +landingTop.displayName = 'LandingTop'; +landingTop.propTypes = propTypes; +export default landingTop; diff --git a/client/src/components/landing/components/Testimonials.js b/client/src/components/landing/components/Testimonials.js new file mode 100644 index 0000000000..e8b83e2ed5 --- /dev/null +++ b/client/src/components/landing/components/Testimonials.js @@ -0,0 +1,61 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { ImageLoader } from '../../helpers'; +import testimonialsMeta from '../landingMeta'; + +const propTypes = { + page: PropTypes.string +}; + +const Testimonials = () => { + const campers = Object.keys(testimonialsMeta); + + return ( +
+

+ Here is what our alumini say about freeCodeCamp: +

+
+ {campers.map((camper, i) => { + let { + name, + country, + position, + company, + testimony + } = testimonialsMeta[camper]; + let imageSource = require(`../../../assets/images/landing/${camper}.png`); + return ( +
+
+ +
+ +
+
+

+ {' '} + {name} in {country} +

+

+ {position} at {company} +

+
+
{testimony}
+
+
+ ); + })} +
+
+ ); +}; + +Testimonials.displayName = 'Testimonals'; +Testimonials.propTypes = propTypes; +export default Testimonials; diff --git a/client/src/components/landing/components/companyLogos.css b/client/src/components/landing/components/companyLogos.css deleted file mode 100644 index a09f398e74..0000000000 --- a/client/src/components/landing/components/companyLogos.css +++ /dev/null @@ -1,32 +0,0 @@ -.logo-row svg { - height: 25px; - margin: 10px; -} - -@media (min-width: 343px) { - .logo-row svg { - height: 35px; - } -} - -@media (min-width: 380px) { - .logo-row svg { - height: 40px; - } -} - -@media (min-width: 480px) { - .logo-row svg { - height: 25px; - } -} - -@media (min-width: 600px) { - .logo-row svg { - height: 40px; - margin: 10px; - } - .logo-row #apple-logo { - height: 35px; - } -} diff --git a/client/src/components/landing/index.js b/client/src/components/landing/index.js index 05e0217302..294539c29f 100644 --- a/client/src/components/landing/index.js +++ b/client/src/components/landing/index.js @@ -1,108 +1,49 @@ import React, { Fragment } from 'react'; - -import { Grid, Row, Col } from '@freecodecamp/react-bootstrap'; +import { Grid } from '@freecodecamp/react-bootstrap'; import Helmet from 'react-helmet'; import PropTypes from 'prop-types'; -import { Link } from 'gatsby'; -import { uniq, partition } from 'lodash'; -import { Spacer } from '../helpers'; -import Login from '../Header/components/Login'; -import CompanyLogos from './components/CompanyLogos'; -import { AsFeatureLogo } from '../../assets/images'; +import { graphql, useStaticQuery } from 'gatsby'; + +import Testimonials from './components/Testimonials'; +import LandingTop from './components/LandingTop'; +import Certifications from './components/Certifications'; +import AsSeenIn from './components/AsSeenIn'; import './landing.css'; -import '../Map/map.css'; const propTypes = { - nodes: PropTypes.array + page: PropTypes.string }; -const BigCallToAction = () => ( - - - - Get started (it's free) - - - -); +export const Landing = ({ page = 'landing' }) => { + const data = useStaticQuery(graphql` + query certifications { + challenges: allChallengeNode( + filter: { isHidden: { eq: false } } + sort: { fields: [superOrder, order, challengeOrder] } + ) { + nodes { + superBlock + } + } + } + `); -const AsFeaturedSection = () => ( - - -
-

As Featured In:

- -
- -
-); - -export const Landing = ({ nodes }) => { - const [superBlocks, rest] = partition( - uniq(nodes.map(node => node.superBlock)), - name => name !== 'Coding Interview Prep' - ); - const interviewPrep = rest[0]; return ( Learn to code at home | freeCodeCamp.org
- - - -

- Welcome to freeCodeCamp.org -

- -

- Learn to code at home. -

-

Build projects.

-

- Earn certifications. -

-

- Since 2014, more than 40,000 freeCodeCamp.org graduates have - gotten jobs at tech companies including: -

- - -
- - - - - - -

Certifications:

-
    - {superBlocks.map((superBlock, i) => ( -
  • - -

    {superBlock}

    - -
  • - ))} -
- -

Additional Learning:

-
    -
  • - -

    {interviewPrep}

    - -
  • -
- -
- + +
+ + + + + +
diff --git a/client/src/components/landing/landing.css b/client/src/components/landing/landing.css index c286027be3..10b72c58bf 100644 --- a/client/src/components/landing/landing.css +++ b/client/src/components/landing/landing.css @@ -9,21 +9,203 @@ white-space: pre-line; } -.logo-row { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-around; - align-content: center; - margin-bottom: 10px; -} - .landing-page ul { list-style: none; padding-left: 0px; } #featured-logos { - margin-top: 10px; max-width: 600px; } +.landing-top h1:first-child { + margin-top: 0px; +} + +.landing-page h1, +.landing-page h2, +.landing-page p { + font-family: 'Lato', sans-serif; +} + +p.caption { + margin: 10px 0 0 0; + font-size: 0.8rem; + color: var(--quaternary-color); +} + +.as-seen-in { + background-color: var(--gray-75); + color: var(--gray-15); +} + +.as-seen-in h1 { + color: var(--gray-15); +} + +.landing-page h1 { + margin-bottom: 40px; +} + +.campers-images { + display: none !important; +} + +@media (min-width: 1200px) { + .campers-images { + display: inline-block !important; + width: 100%; + height: 100%; + } +} + +.logo-row { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + align-content: center; + margin: 10px 0; +} + +.logo-row svg { + height: 40px; +} + +@media (min-width: 370px) { + .logo-row svg { + margin: 5px 15px; + } +} + +@media (min-width: 480px) { + .logo-row svg { + height: 25px; + margin: 5px; + } +} + +@media (min-width: 550px) { + .logo-row { + justify-content: space-between; + } + + .logo-row svg { + height: 40px; + } +} + +.cta-landing-section h2 { + font-size: 1.3rem; + font-weight: 400; +} + +/* testimonials */ + +.testimonials-row { + display: flex; + flex-direction: column; + justify-content: space-around; + flex-wrap: wrap; + align-items: center; +} + +.testimonials p, +.testimonials strong { + font-family: 'Lato', sans-serif; + font-size: 1.1rem; + margin: 0; + color: var(--gray-75); +} +.testimonial-meta p:last-child { + margin-top: 15px; +} + +.testimonial-card { + max-width: 350px; + display: flex; + flex-direction: column; + margin: 10px 10px 50px 10px; + background: var(--gray-00); + -webkit-box-shadow: 0 3px 13px 1px rgba(0, 0, 0, 0.09); + box-shadow: 0 3px 13px 1px rgba(0, 0, 0, 0.09); +} + +.testimonial-image, +.landing-page-image { + width: 100%; +} + +.testimonial-meta { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 10px 0px 30px 0px; +} + +.testimonial-meta p, +.testimonial-meta strong { + font-size: 1.2rem; +} + +.testimonial-card-header { + height: 100%; + padding-bottom: 100%; + position: relative; +} + +.testimonial-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: auto; +} + +.testimonials-footer { + display: flex; + flex-direction: column; + padding: 25px; + text-align: justify; + justify-content: center; +} +.landing-top, +.as-seen-in, +.certification-section, +.testimonials { + padding: 4vw 0; +} + +@media (min-width: 500px) { + .cta-landing-section h2 { + font-size: 1.5rem; + } +} + +@media (min-width: 992px) { + .testimonial-card { + flex-direction: row; + width: auto; + max-width: none; + } + .testimonial-image { + width: auto; + height: 100%; + } + .testimonial-meta { + padding: 0px 0px 30px; + } + .testimonial-meta p, + .testimonial-meta strong { + font-size: 1.3rem; + } + .testimonials-footer { + padding: 40px; + } + .testimonial-card-header { + height: 350px; + min-width: 350px; + padding-bottom: 0; + } +} diff --git a/client/src/components/landing/landingMeta.js b/client/src/components/landing/landingMeta.js new file mode 100644 index 0000000000..4a8ad26481 --- /dev/null +++ b/client/src/components/landing/landingMeta.js @@ -0,0 +1,48 @@ +import React from 'react'; + +export default { + Shawn: { + name: 'Shawn Wang', + country: 'Singapore', + position: 'Software Engineer', + company: 'Amazon', + testimony: ( +

+ "It's scary to change careers. I only gained confidence that I could + code by working through the hundreds of hours of free lessons on + freeCodeCamp. Within a year I had a six-figure job as a Software + Engineer. + freeCodeCamp changed my life." +

+ ) + }, + Sarah: { + name: 'Sarah Chima', + country: 'Nigeria', + position: 'Software Engineer', + company: 'ChatDesk', + testimony: ( +

+ “freeCodeCamp was the gateway to my career + as a software developer. The well-structured curriculum took my coding + knowledge from a total beginner level to a very confident level. It was + everything I needed to land my first dev job at an amazing company." +

+ ) + }, + Emma: { + name: 'Emma Bostian', + country: 'Sweden', + position: 'Software Engineer', + company: 'Spotify', + testimony: ( +

+ "I've always struggled with learning JavaScript. I've taken many courses + but freeCodeCamp's course was the one which stuck. Studying JavaScript + as well as data structures and algorithms on{' '} + freeCodeCamp gave me the skills and confidence I needed + to land my dream job as a software engineer at Spotify." +

+ ) + } +}; diff --git a/client/src/components/layouts/global.css b/client/src/components/layouts/global.css index b64835343c..12fd545e55 100644 --- a/client/src/components/layouts/global.css +++ b/client/src/components/layouts/global.css @@ -11,7 +11,7 @@ body { .btn-cta-big { max-height: 100%; - font-size: 1.3rem; + font-size: 1.5rem; white-space: normal; width: 100%; } @@ -64,25 +64,13 @@ p { } .big-heading { - font-size: 1.5rem !important; + font-size: 2.5rem !important; overflow-wrap: break-word; } -.medium-heading { - font-size: 1.25rem !important; - font-weight: normal; -} - -.medium-heading.text-center { - margin-bottom: 50px; -} - @media (max-width: 500px) { .big-heading { - font-size: 1.2rem !important; - } - .medium-heading { - font-size: 1.1rem !important; + font-size: 1.5rem !important; } h1 { font-size: 1.3rem; @@ -99,6 +87,12 @@ p { } } +@media (max-width: 1199px) { + .btn-cta-big { + font-size: 1.3rem; + } +} + .text-center { text-align: center !important; } @@ -160,6 +154,7 @@ input[type='submit'], color: var(--secondary-color); border-radius: 0px; text-decoration: none; + white-space: pre-line; } button:hover, @@ -257,6 +252,33 @@ fieldset[disabled] .btn-primary.focus { color: inherit; } +.link-btn { + margin-bottom: 10px; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + font-size: 1rem; + text-align: left; +} + +.link-btn.btn-lg svg { + height: 100%; + min-height: 20px; + min-width: 16px; + margin-left: 5px; +} + +.link-btn:hover svg { + fill: var(--quaternary-background); +} + +@media (min-width: 700px) { + .link-btn { + font-size: 1.1rem; + } +} + .button-group .btn:not(:last-child) { margin-bottom: 10px; } @@ -494,27 +516,3 @@ blockquote .small { #search::placeholder { color: var(--secondary-color); } - -/* checkbox */ - -.checkbox-inline input[type='checkbox'] { - margin-left: -30px; -} - -.checkbox-inline { - padding-left: 30px; -} - -input[type='checkbox'] { - height: 21px; - width: 21px; -} - -/* general page styling */ -.default-page-wrapper { - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: center; - padding: 20px; -} diff --git a/client/src/pages/index.js b/client/src/pages/index.js index 6d78449a82..2450a048ce 100644 --- a/client/src/pages/index.js +++ b/client/src/pages/index.js @@ -1,16 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { graphql } from 'gatsby'; import Landing from '../components/landing'; import { AllChallengeNode } from '../redux/propTypes'; -export const IndexPage = ({ - data: { - allChallengeNode: { nodes } - } -}) => { - return ; +export const IndexPage = () => { + return ; }; const propTypes = { @@ -23,16 +18,3 @@ IndexPage.propTypes = propTypes; IndexPage.displayName = 'IndexPage'; export default IndexPage; - -export const query = graphql` - query MyQuery { - allChallengeNode( - filter: { isHidden: { eq: false } } - sort: { fields: [superOrder, order, challengeOrder] } - ) { - nodes { - superBlock - } - } - } -`; diff --git a/client/utils/gatsby/layoutSelector.js b/client/utils/gatsby/layoutSelector.js index 4130fb4ca1..6fa4a90c9b 100644 --- a/client/utils/gatsby/layoutSelector.js +++ b/client/utils/gatsby/layoutSelector.js @@ -32,7 +32,7 @@ export default function layoutSelector({ element, props }) { ); } - if (/^\/donation(\/.*)*|^\/donate(\/.*)*/.test(pathname)) { + if (/^\/donation(\/.*)*|^\/$|^\/donate(\/.*)*/.test(pathname)) { return ( {element} diff --git a/cypress/integration/landing.js b/cypress/integration/landing.js index c09f5ca268..2b9d20892c 100644 --- a/cypress/integration/landing.js +++ b/cypress/integration/landing.js @@ -1,16 +1,41 @@ /* global cy */ const selectors = { heading: "[data-test-label='landing-header']", - callToAction: "[data-test-label='landing-big-cta']" + callToAction: "[data-test-label='landing-big-cta']", + certifications: "[data-test-label='certifications']", + testimonials: "[data-test-label='testimonial-cards']", + landingPageImage: '.landing-page-image' }; describe('Landing page', function() { it('renders', function() { cy.visit('/'); - cy.title().should('eq', 'Learn to code at home | freeCodeCamp.org'); - - cy.contains(selectors.heading, 'Welcome to freeCodeCamp.org'); + cy.contains(selectors.heading, 'Learn to code at home.'); cy.contains(selectors.callToAction, "Get started (it's free)"); + cy.get(selectors.callToAction).should('have.length', 2); + }); + + it('has a visible large image on large viewports', function() { + cy.viewport(1200, 660) + .get(selectors.landingPageImage) + .should('be.visible'); + + cy.viewport(1199, 660) + .get(selectors.landingPageImage) + .should('not.be.visible'); + }); + + it('has 10 certifications', function() { + cy.get(selectors.certifications) + .children() + .its('length') + .should('eq', 10); + }); + it('has 3 testimonial cards', function() { + cy.get(selectors.testimonials) + .children() + .its('length') + .should('eq', 3); }); });