diff --git a/.eslintrc b/.eslintrc index fdb626f..e5dd9e5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -202,7 +202,7 @@ "react/jsx-sort-props":["off"], "react/jsx-uses-react":["error"], "react/jsx-uses-vars":["error"], - "react/jsx-curly-brace-presence":["error", { "props": "always", "children": "always" }], + "react/jsx-curly-brace-presence":["error", { "props": "always" }], "react/no-did-mount-set-state":["off"], "react/no-did-update-set-state":["error"], "react/no-multi-comp":["off"], diff --git a/src/components/Answer/Answer.component.js b/src/components/Answer/Answer.component.js new file mode 100644 index 0000000..c5eb069 --- /dev/null +++ b/src/components/Answer/Answer.component.js @@ -0,0 +1,91 @@ +import React, { PureComponent } from "react"; + +import Highlight from "react-highlight"; + +import Markdown from "react-remarkable"; + +import "highlight.js/styles/github-gist.css"; + +import PropTypes from "prop-types"; + +import "./Answer.css"; + +import classNames from "classnames"; + +const letterMap = { + 1: `a`, + 2: `b`, + 3: `c`, + 4: `d`, +}; + +export default class Answer extends PureComponent { + static propTypes = { + questionId: PropTypes.string, + answerNumber: PropTypes.number.isRequired, + selectedAnswer: PropTypes.number, + text: PropTypes.string.isRequired, + answerType: PropTypes.string.isRequired, + theme: PropTypes.oneOf([`none`, `green`, `red`, `blue-outline`]) + .isRequired, + onChange: PropTypes.func, + }; + + static defaultProps = { + theme: `none`, + answerType: `markdown`, + }; + + handleOnChange = () => { + const { onChange, answerNumber } = this.props; + + onChange(answerNumber); + }; + + render() { + const { + onChange, + answerType, + text, + questionId, + answerNumber, + selectedAnswer, + theme, + } = this.props; + + const answerIsSelected = answerNumber === selectedAnswer; + + const textEl = + answerType === `code` ? ( + {text} + ) : ( + {text} + ); + + const radioId = `${questionId}-${answerNumber}`; + + const containerClasses = classNames(`Answer`, `--theme-${theme}`); + + const content = ( +
+ + {letterMap[answerNumber]} + + {onChange && ( + + )} +
{textEl}
+
+ ); + + return onChange ? : content; + } +} diff --git a/src/components/Answer/Answer.css b/src/components/Answer/Answer.css new file mode 100644 index 0000000..7d12764 --- /dev/null +++ b/src/components/Answer/Answer.css @@ -0,0 +1,63 @@ +.Answer { + display: block; + background-color: #F7F7F7; + border: 2px solid #F1F1F1; + padding: 20px; + position: relative; + border-radius: 5px; + transition: border-color .2s; +} + +.Answer.--theme-blue-outline { + border-color: var(--blue) +} + +.Answer.--theme-red { + border-color: rgba(187, 0, 23, .4); + background-color: rgba(187, 0, 23, .2); +} + +.Answer.--theme-green { + border-color: rgba(128, 174, 82, .4); + background-color: rgba(128, 174, 82, .2); +} + +.Answer__question-number { + position: absolute; + top: 4px; + left: 4px; + font-size: .625rem; + line-height: 1; + text-transform: uppercase; + color: #B3B3B3; + font-weight: 900; + transition: color .2s; +} + +.Answer.--theme-blue-outline .Answer__question-number { + color: var(--blue) +} + +.Answer.--theme-red .Answer__question-number { + color: var(--red); +} + +.Answer.--theme-green .Answer__question-number { + color: var(--green); +} + +.Answer code { + background-color: rgba(255, 255, 255, .8); +} + +.Answer.--theme-red code { + background-color: rgba(187, 0, 23, .2); +} + +.Answer.--theme-green code { + background-color: rgba(128, 174, 82, .2); +} + +.Answer__text .hljs { + background-color: transparent !important; +} diff --git a/src/components/Answer/index.js b/src/components/Answer/index.js new file mode 100644 index 0000000..47c0bb2 --- /dev/null +++ b/src/components/Answer/index.js @@ -0,0 +1 @@ +export { default as Answer } from "./Answer.component"; diff --git a/src/components/CodeFigure/CodeFigure.component.js b/src/components/CodeFigure/CodeFigure.component.js new file mode 100644 index 0000000..f8ee990 --- /dev/null +++ b/src/components/CodeFigure/CodeFigure.component.js @@ -0,0 +1,23 @@ +import React, { PureComponent } from "react"; + +import Highlight from "react-highlight"; + +import PropTypes from "prop-types"; + +import "./CodeFigure.css"; + +export default class CodeFigure extends PureComponent { + static propTypes = { + codeFigure: PropTypes.string.isRequired, + }; + + render() { + const { codeFigure } = this.props; + + return ( +
+ {codeFigure} +
+ ); + } +} diff --git a/src/components/CodeFigure/CodeFigure.css b/src/components/CodeFigure/CodeFigure.css new file mode 100644 index 0000000..e0890a0 --- /dev/null +++ b/src/components/CodeFigure/CodeFigure.css @@ -0,0 +1,72 @@ +.CodeFigure > pre { + margin: 0; +} + +.CodeFigure .hljs { + display: block; + overflow-x: auto; + background: #2b2b2b; + padding: 20px 15px; + border-radius: 5px; + color: #bababa; +} + +.CodeFigure .hljs-strong, +.CodeFigure .hljs-emphasis { + color: #a8a8a2; +} + +.CodeFigure .hljs-bullet, +.CodeFigure .hljs-quote, +.CodeFigure .hljs-link, +.CodeFigure .hljs-number, +.CodeFigure .hljs-regexp, +.CodeFigure .hljs-literal { + color: #6896ba; +} + +.CodeFigure .hljs-code, +.CodeFigure .hljs-selector-class { + color: #a6e22e; +} + +.CodeFigure .hljs-emphasis { + font-style: italic; +} + +.CodeFigure .hljs-keyword, +.CodeFigure .hljs-selector-tag, +.CodeFigure .hljs-section, +.CodeFigure .hljs-attribute, +.CodeFigure .hljs-name, +.CodeFigure .hljs-variable { + color: #cb7832; +} + +.CodeFigure .hljs-params { + color: #b9b9b9; +} + +.CodeFigure .hljs-string { + color: #6a8759; +} + +.CodeFigure .hljs-subst, +.CodeFigure .hljs-type, +.CodeFigure .hljs-built_in, +.CodeFigure .hljs-builtin-name, +.CodeFigure .hljs-symbol, +.CodeFigure .hljs-selector-id, +.CodeFigure .hljs-selector-attr, +.CodeFigure .hljs-selector-pseudo, +.CodeFigure .hljs-template-tag, +.CodeFigure .hljs-template-variable, +.CodeFigure .hljs-addition { + color: #e0c46c; +} + +.CodeFigure .hljs-comment, +.CodeFigure .hljs-deletion, +.CodeFigure .hljs-meta { + color: #7f7f7f; +} diff --git a/src/components/CodeFigure/index.js b/src/components/CodeFigure/index.js new file mode 100644 index 0000000..a59dc6d --- /dev/null +++ b/src/components/CodeFigure/index.js @@ -0,0 +1 @@ +export { default as CodeFigure } from "./CodeFigure.component"; diff --git a/src/components/Completed/Completed/Completed.component.js b/src/components/Completed/Completed/Completed.component.js index 386f8e6..388a53c 100644 --- a/src/components/Completed/Completed/Completed.component.js +++ b/src/components/Completed/Completed/Completed.component.js @@ -1,20 +1,15 @@ import React, { PureComponent } from "react"; - -import { - CompletedHeader, - Results, -} from "components"; +import { CompletedHeader, Results } from "components"; import "./Completed.css"; export default class Completed extends PureComponent { render() { - return (
- - + +
); } diff --git a/src/components/Completed/Completed/index.js b/src/components/Completed/Completed/index.js index 68076b8..90fd48b 100644 --- a/src/components/Completed/Completed/index.js +++ b/src/components/Completed/Completed/index.js @@ -1,2 +1 @@ -export { default as Completed } from './Completed.container'; - +export { default as Completed } from "./Completed.container"; diff --git a/src/components/Completed/CompletedHeader/CompletedHeader.component.js b/src/components/Completed/CompletedHeader/CompletedHeader.component.js index 7714d49..1392b3a 100644 --- a/src/components/Completed/CompletedHeader/CompletedHeader.component.js +++ b/src/components/Completed/CompletedHeader/CompletedHeader.component.js @@ -1,12 +1,10 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from "react"; -import PropTypes from 'prop-types'; +import PropTypes from "prop-types"; -import { +import {} from "components"; -} from 'components'; - -import './CompletedHeader.css'; +import "./CompletedHeader.css"; export default class CompletedHeader extends PureComponent { static propTypes = { @@ -14,36 +12,38 @@ export default class CompletedHeader extends PureComponent { }; render() { - const {name} = this.props; + const { name } = this.props; return (
-

{`Congratulations ${name}, you're finished!`}

+

{`Congratulations ${name}, you're finished!`}

{`Your Coding Strength is: `} - {`Javascript Proficiency`} + Javascript Proficiency

- Based on your responses, you have solid - understanding of the foundational JavaScript - essentials you need to learn mobile app development. - Consider getting started with a training program - that offers opportunities for practical, real world - experience building apps. + Based on your responses, you have solid understanding of + the foundational JavaScript essentials you need to learn + mobile app development. Consider getting started with a + training program that offers opportunities for + practical, real world experience building apps.

- You can review your results below, and we also sent - a recap email with your responses to your inbox. If - you want me to review your assessment results and - provide completely personalized recommendations to - make sure you get started right, click below to book - a {`free 15-minute coaching session`}{` `} + You can review your results below, and we also sent a + recap email with your responses to your inbox. If you + want me to review your assessment results and provide + completely personalized recommendations to make sure you + get started right, click below to book a{` `} + free 15-minute coaching session + {` `} with me!

Schedule a free coaching call diff --git a/src/components/Completed/CompletedHeader/CompletedHeader.container.js b/src/components/Completed/CompletedHeader/CompletedHeader.container.js index 4daa550..1e68650 100644 --- a/src/components/Completed/CompletedHeader/CompletedHeader.container.js +++ b/src/components/Completed/CompletedHeader/CompletedHeader.container.js @@ -1,12 +1,9 @@ -import { connect } from 'react-redux'; +import { connect } from "react-redux"; -import { - quizNameSelector, -} from 'selectors'; +import { quizNameSelector } from "selectors"; - -import CompletedHeader from './CompletedHeader.component'; +import CompletedHeader from "./CompletedHeader.component"; export default connect((st) => ({ name: quizNameSelector(st), -}), {})(CompletedHeader); +}))(CompletedHeader); diff --git a/src/components/Completed/CompletedHeader/index.js b/src/components/Completed/CompletedHeader/index.js index 3c07862..98d8614 100644 --- a/src/components/Completed/CompletedHeader/index.js +++ b/src/components/Completed/CompletedHeader/index.js @@ -1 +1 @@ -export { default as CompletedHeader } from './CompletedHeader.container'; +export { default as CompletedHeader } from "./CompletedHeader.container"; diff --git a/src/components/Completed/CompletedResultsList/CompletedResultsList.component.js b/src/components/Completed/CompletedResultsList/CompletedResultsList.component.js index 7de2e65..e87e631 100644 --- a/src/components/Completed/CompletedResultsList/CompletedResultsList.component.js +++ b/src/components/Completed/CompletedResultsList/CompletedResultsList.component.js @@ -1,19 +1,29 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from "react"; -import PropTypes from 'prop-types'; +import PropTypes from "prop-types"; -import { - questions, -} from 'questions'; +import classNames from "classnames"; -import './CompletedResultsList.css'; +import Markdown from "react-remarkable"; + +import { questions } from "questions"; + +import { Question, Answer, CodeFigure } from "components"; + +import "./CompletedResultsList.css"; + +const greenCheckmark = require(`./green-checkmark.svg`); + +const redX = require(`./red-x.svg`); export default class CompletedResultsList extends PureComponent { static propTypes = { - answers: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - selectedAnswer: PropTypes.number.isRequired, - })).isRequired, + answers: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + selectedAnswer: PropTypes.number.isRequired, + }) + ).isRequired, }; render() { @@ -21,12 +31,119 @@ export default class CompletedResultsList extends PureComponent { return (
    - {answers.map((answer, i) => { - const { question } = questions.find(q => q.id === answer.id); + {answers.map(({ selectedAnswer, id }, i) => { + const { + question, + correctAnswer, + answers: questionAnswers, + answerType, + codeFigure, + incorrectFeedback, + } = questions.find((q) => q.id === id); + + const correctAnswerText = + questionAnswers[correctAnswer - 1]; + const userAnswerText = questionAnswers[selectedAnswer - 1]; + const answeredCorrectly = correctAnswer === selectedAnswer; + + const answersWrapperClasses = classNames( + `CompletedResultsList__answers-wrapper`, + { + [`--no-grid`]: !!codeFigure, + } + ); return ( -
  1. -

    {`${i + 1}. ${question}`}

    +
  2. +
    +

    + +

    +
    + {codeFigure && ( +
    + +
    + )} +
    +
    +
    +

    + Correct Answer +

    + +
    +
    +

    + Your Answer +

    + +
    +
    + {!answeredCorrectly && ( +
    +

    + Feedback +

    +
    + + {incorrectFeedback} + +
    +
    + )} +
    +
    +
  3. ); })} diff --git a/src/components/Completed/CompletedResultsList/CompletedResultsList.container.js b/src/components/Completed/CompletedResultsList/CompletedResultsList.container.js index 6ca075b..f095d71 100644 --- a/src/components/Completed/CompletedResultsList/CompletedResultsList.container.js +++ b/src/components/Completed/CompletedResultsList/CompletedResultsList.container.js @@ -1,10 +1,12 @@ -import { connect } from 'react-redux'; +import { connect } from "react-redux"; -import {quizAnswersSelector} from 'selectors'; +import { quizAnswersSelector } from "selectors"; +import CompletedResultsList from "./CompletedResultsList.component"; -import CompletedResultsList from './CompletedResultsList.component'; - -export default connect((st) => ({ - answers: quizAnswersSelector(st) -}), {})(CompletedResultsList); +export default connect( + (st) => ({ + answers: quizAnswersSelector(st), + }), + {} +)(CompletedResultsList); diff --git a/src/components/Completed/CompletedResultsList/CompletedResultsList.css b/src/components/Completed/CompletedResultsList/CompletedResultsList.css index f2d832a..a67fbd1 100644 --- a/src/components/Completed/CompletedResultsList/CompletedResultsList.css +++ b/src/components/Completed/CompletedResultsList/CompletedResultsList.css @@ -4,9 +4,55 @@ } .CompletedResultsList > li { - margin: 0 + margin: 0 0 48px; +} + +.CompletedResultsList__container { + padding-left: 48px; + background-repeat: no-repeat; + background-size: 24px 24px; + background-position: top left; + max-width: 800px; } .CompletedResultsList__question-title { margin-bottom: 20px } + +.CompletedResultsList__content { + display: flex; +} + +.CompletedResultsList__code-figure { + margin-right: 35px; +} + +.CompletedResultsList__answers { + flex: 1; +} + +.CompletedResultsList__answers-wrapper { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 24px; + margin-bottom: 24px; +} + +.CompletedResultsList__answers-wrapper.--no-grid { + grid-template-columns: 1fr; + grid-template-rows: auto auto auto; + grid-row-gap: 24px; + margin-bottom: 0; +} + +.CompletedResultsList__feedback-title, +.CompletedResultsList__answer-title { + font-weight: 700; + font-size: .8rem; + margin-bottom: 4px; +} + +.CompletedResultsList__feedback a { + color: var(--blue) +} diff --git a/src/components/Completed/CompletedResultsList/green-checkmark.svg b/src/components/Completed/CompletedResultsList/green-checkmark.svg new file mode 100644 index 0000000..165d609 --- /dev/null +++ b/src/components/Completed/CompletedResultsList/green-checkmark.svg @@ -0,0 +1,16 @@ + + + + + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Completed/CompletedResultsList/index.js b/src/components/Completed/CompletedResultsList/index.js index c54914b..9726767 100644 --- a/src/components/Completed/CompletedResultsList/index.js +++ b/src/components/Completed/CompletedResultsList/index.js @@ -1 +1,3 @@ -export { default as CompletedResultsList } from './CompletedResultsList.container'; +export { + default as CompletedResultsList, +} from "./CompletedResultsList.container"; diff --git a/src/components/Completed/CompletedResultsList/red-x.svg b/src/components/Completed/CompletedResultsList/red-x.svg new file mode 100644 index 0000000..cdfa94b --- /dev/null +++ b/src/components/Completed/CompletedResultsList/red-x.svg @@ -0,0 +1,16 @@ + + + +  copy + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Completed/Results/Results.component.js b/src/components/Completed/Results/Results.component.js index 4edb12c..3df5eb9 100644 --- a/src/components/Completed/Results/Results.component.js +++ b/src/components/Completed/Results/Results.component.js @@ -1,21 +1,15 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent } from "react"; -import PropTypes from 'prop-types'; +import { CompletedResultsList } from "components"; -import { - CompletedResultsList -} from 'components'; - -import './Results.css'; +import "./Results.css"; export default class Results extends PureComponent { - render() { - return (
    -

    {`Quiz Results`}

    - +

    Quiz Results

    +
    ); } diff --git a/src/components/Completed/Results/index.js b/src/components/Completed/Results/index.js index dc398fe..55799c0 100644 --- a/src/components/Completed/Results/index.js +++ b/src/components/Completed/Results/index.js @@ -1 +1 @@ -export { default as Results } from './Results.component'; +export { default as Results } from "./Results.component"; diff --git a/src/components/Completed/index.js b/src/components/Completed/index.js index a9cf574..9a3be6e 100644 --- a/src/components/Completed/index.js +++ b/src/components/Completed/index.js @@ -1,4 +1,4 @@ -export * from './Completed'; -export * from './CompletedHeader'; -export * from './CompletedResultsList'; -export * from './Results'; +export * from "./Completed"; +export * from "./CompletedHeader"; +export * from "./CompletedResultsList"; +export * from "./Results"; diff --git a/src/components/EmailCapture/index.js b/src/components/EmailCapture/index.js index caa7ae8..6ee57e5 100644 --- a/src/components/EmailCapture/index.js +++ b/src/components/EmailCapture/index.js @@ -1,3 +1,3 @@ -export * from './EmailCapture'; -export * from './EmailCaptureContinueButton'; -export * from './EmailCaptureEmailInput'; +export * from "./EmailCapture"; +export * from "./EmailCaptureContinueButton"; +export * from "./EmailCaptureEmailInput"; diff --git a/src/components/NameCapture/index.js b/src/components/NameCapture/index.js index 2096548..9737780 100644 --- a/src/components/NameCapture/index.js +++ b/src/components/NameCapture/index.js @@ -1,3 +1,3 @@ -export * from './NameCapture'; -export * from './NameCaptureContinueButton'; -export * from './NameCaptureNameInput'; +export * from "./NameCapture"; +export * from "./NameCaptureContinueButton"; +export * from "./NameCaptureNameInput"; diff --git a/src/components/Question/Question.component.js b/src/components/Question/Question.component.js new file mode 100644 index 0000000..4701c44 --- /dev/null +++ b/src/components/Question/Question.component.js @@ -0,0 +1,23 @@ +import React, { PureComponent } from "react"; + +import Markdown from "react-remarkable"; + +import PropTypes from "prop-types"; + +import "./Question.css"; + +export default class Question extends PureComponent { + static propTypes = { + question: PropTypes.string.isRequired, + }; + + render() { + const { question } = this.props; + + return ( +
    + {question} +
    + ); + } +} diff --git a/src/components/Question/Question.css b/src/components/Question/Question.css new file mode 100644 index 0000000..6821d42 --- /dev/null +++ b/src/components/Question/Question.css @@ -0,0 +1,3 @@ +.Question code { + background-color: #f7f9fa; +} diff --git a/src/components/Question/index.js b/src/components/Question/index.js new file mode 100644 index 0000000..5f61d2a --- /dev/null +++ b/src/components/Question/index.js @@ -0,0 +1 @@ +export { default as Question } from "./Question.component"; diff --git a/src/components/QuestionAnswer/QuestionAnswer.component.js b/src/components/QuestionAnswer/QuestionAnswer.component.js index 920519f..1c7d027 100644 --- a/src/components/QuestionAnswer/QuestionAnswer.component.js +++ b/src/components/QuestionAnswer/QuestionAnswer.component.js @@ -1,87 +1,27 @@ import React, { PureComponent } from "react"; -import Highlight from "react-highlight"; - -import Markdown from "react-remarkable"; - import PropTypes from "prop-types"; -import "./QuestionAnswer.css"; -import classNames from "classnames"; - -const letterMap = { - 1: `a`, - 2: `b`, - 3: `c`, - 4: `d`, -}; +import { Answer } from "components"; export default class QuestionAnswer extends PureComponent { static propTypes = { - questionId: PropTypes.string.isRequired, answerNumber: PropTypes.number.isRequired, selectedAnswer: PropTypes.number, text: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, }; - static defaultProps = { - type: `markdown`, - }; - - handleOnChange = () => { - const { onChange, answerNumber } = this.props; - - onChange(answerNumber); - }; - render() { - const { - type, - text, - questionId, - answerNumber, - selectedAnswer, - } = this.props; - - const answerIsSelected = answerNumber === selectedAnswer; - const codeAnswer = type === `code`; - - const textEl = codeAnswer ? ( - {text} - ) : ( - {text} - ); - - const radioId = `${questionId}-${answerNumber}`; - - const containerClasses = classNames(`QuestionAnswer`, { - [`--selected`]: answerIsSelected, - }); - - const questionNumberClasses = classNames( - `QuestionAnswer-question-number`, - { - [`--selected`]: answerIsSelected, - } - ); + const { selectedAnswer, answerNumber } = this.props; return ( - + ); } } diff --git a/src/components/QuestionAnswer/QuestionAnswer.css b/src/components/QuestionAnswer/QuestionAnswer.css deleted file mode 100644 index c98c1fa..0000000 --- a/src/components/QuestionAnswer/QuestionAnswer.css +++ /dev/null @@ -1,37 +0,0 @@ -.QuestionAnswer { - display: block; - background-color: #F7F7F7; - border: 2px solid #F1F1F1; - padding: 20px; - position: relative; - border-radius: 5px; - transition: border-color .2s; -} - -.QuestionAnswer.--selected { - border-color: var(--blue) -} - -.QuestionAnswer-question-number { - position: absolute; - top: 4px; - left: 4px; - font-size: .625rem; - line-height: 1; - text-transform: uppercase; - color: #B3B3B3; - font-weight: 900; - transition: color .2s; -} - -.QuestionAnswer-question-number.--selected { - color: var(--blue) -} - -.QuestionAnswer__text strong { - font-weight: 700; -} - -.QuestionAnswer__text em { - font-style: italic; -} diff --git a/src/components/Quiz/Quiz.component.js b/src/components/Quiz/Quiz.component.js index c5ebca3..7ad94eb 100644 --- a/src/components/Quiz/Quiz.component.js +++ b/src/components/Quiz/Quiz.component.js @@ -64,7 +64,7 @@ export default class Quiz extends PureComponent { return (
    -

    {`Javascript Quiz`}

    +

    Javascript Quiz

    {content}
    ); diff --git a/src/components/QuizProgressIndicator/QuizProgressIndicator.component.js b/src/components/QuizProgressIndicator/QuizProgressIndicator.component.js index bb644ad..23f77c0 100644 --- a/src/components/QuizProgressIndicator/QuizProgressIndicator.component.js +++ b/src/components/QuizProgressIndicator/QuizProgressIndicator.component.js @@ -21,14 +21,12 @@ export default class QuizProgressIndicator extends PureComponent { style={{ backgroundImage: `url(${bg})` }} >

    - {`Question number `} + Question number {totalAnswers + 1} - {` out of `} + out of {totalQuestions} diff --git a/src/components/QuizQuestion/QuizQuestion.component.js b/src/components/QuizQuestion/QuizQuestion.component.js index e3fa2f9..f126a3b 100644 --- a/src/components/QuizQuestion/QuizQuestion.component.js +++ b/src/components/QuizQuestion/QuizQuestion.component.js @@ -1,12 +1,6 @@ import React, { PureComponent } from "react"; -import Highlight from "react-highlight"; - -import Markdown from "react-remarkable"; - -import { QuestionAnswer, ActionButton } from "components"; - -import "highlight.js/styles/darkula.css"; +import { QuestionAnswer, ActionButton, CodeFigure, Question } from "components"; import PropTypes from "prop-types"; @@ -39,13 +33,7 @@ export default class QuizQuestion extends PureComponent { render() { const { - question: { - question: questionString, - codeFigure, - answers, - id, - answerType, - }, + question: { question, codeFigure, answers, id, answerType }, questionNumber, selectedAnswer, userAnswers, @@ -71,13 +59,11 @@ export default class QuizQuestion extends PureComponent { >{`Thanks ${name}! Let's get started...`}

    )}
    - {questionString} +
    {codeFigure && (
    - - {codeFigure} - +
    )}
    @@ -85,8 +71,9 @@ export default class QuizQuestion extends PureComponent { {answers.map((answer = {}, i) => (
  4. @@ -99,7 +86,7 @@ export default class QuizQuestion extends PureComponent { disabled={typeof selectedAnswer !== `number`} onClick={this.handleOnSubmit} > - Continue + {`Continue`}
diff --git a/src/components/QuizQuestion/QuizQuestion.css b/src/components/QuizQuestion/QuizQuestion.css index 8e3363f..72ab6c2 100644 --- a/src/components/QuizQuestion/QuizQuestion.css +++ b/src/components/QuizQuestion/QuizQuestion.css @@ -31,10 +31,7 @@ } } -.QuizQuestion__question code { - background-color: #f7f9fa; - padding: 0 4px; -} + .QuizQuestion__question p { font-size: 2rem; @@ -60,14 +57,6 @@ margin: 20px 0; } -.QuizQuestion__code-figure > pre { - margin: 0; -} - -.QuizQuestion__code-figure .hljs { - padding: 20px 15px; - border-radius: 5px; -} .QuizQuestion__answers-list-wrapper { display: flex; @@ -77,40 +66,23 @@ .QuizQuestion__answers-list { list-style-type: none; flex: 1; -} - -.QuizQuestion__answers-list.--grid { display: grid; - grid-template-columns: 50% 50%; - list-style-type: none; -} - -@media (max-width: 600px) { - .QuizQuestion__answers-list.--grid { - display: block; - } + grid-row-gap: 24px; + margin-bottom: 24px; } .QuizQuestion__answers-list > li { - flex: 1; - margin: 0 0 25px; -} - -.QuizQuestion__answers-list.--grid > li:nth-child(odd) { - margin-right: 15px; + margin: 0; } -.QuizQuestion__answers-list.--grid > li:nth-child(even) { - margin-left: 15px; +.QuizQuestion__answers-list.--grid { + grid-template-columns: 1fr 1fr; + grid-column-gap: 24px; } @media (max-width: 600px) { - .QuizQuestion__answers-list.--grid > li:nth-child(odd) { - margin-right: 0; - } - - .QuizQuestion__answers-list.--grid > li:nth-child(even) { - margin-left: 0; + .QuizQuestion__answers-list.--grid { + grid-template-columns: auto; } } diff --git a/src/components/index.js b/src/components/index.js index a290448..f8e23ed 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,11 +1,14 @@ -export * from './ActionButton'; -export * from './ActivityIndicator'; -export * from './App'; -export * from './Completed'; -export * from './EmailCapture'; -export * from './NameCapture'; -export * from './QuestionAnswer'; -export * from './Quiz'; -export * from './QuizProgressIndicator'; -export * from './QuizQuestion'; -export * from './TextInput'; +export * from "./ActionButton"; +export * from "./ActivityIndicator"; +export * from "./Answer"; +export * from "./App"; +export * from "./CodeFigure"; +export * from "./Completed"; +export * from "./EmailCapture"; +export * from "./NameCapture"; +export * from "./Question"; +export * from "./QuestionAnswer"; +export * from "./Quiz"; +export * from "./QuizProgressIndicator"; +export * from "./QuizQuestion"; +export * from "./TextInput"; diff --git a/src/index.css b/src/index.css index d99a1ef..4ea3129 100644 --- a/src/index.css +++ b/src/index.css @@ -4,6 +4,8 @@ --blue: #37B9F1; --light-blue: #9ADCF8; --very-dark-blue: #2F658C; + --red: #BB0017; + --green: #80AE52; --minTouchSize: 48px; } @@ -41,6 +43,7 @@ pre > .hljs { } code { + padding: 0 4px; font-family: SourceCodePro, monospace; } diff --git a/src/questions.js b/src/questions.js index 9c62e1d..8239f64 100644 --- a/src/questions.js +++ b/src/questions.js @@ -3,21 +3,13 @@ export const version = 1; export const questions = [ { id: `basic-variable`, - question: `Select the answer below that would set a variable named \`myVariable\` to \`3\`:`, + question: `Select the answer below that would set a variable named \`myVariable\` to 3:`, incorrectFeedback: `This a very basic question and if this answer wasn't on accident, the rest of the quiz is probably going to be very difficult. Start by reading up on the [fundamentals of Javascript](https://www.w3schools.com/js/)`, answers: [ - { - text: `\`myVariable(3)\``, - }, - { - text: `\`var myVariable = 3;\``, - }, - { - text: `\`var myVariable[3]\``, - }, - { - text: `\`var myVariable = {\n 3\n}\``, - }, + `myVariable(3)`, + `var myVariable = 3;`, + `var myVariable[3]`, + `var myVariable = {\n 3\n}`, ], answerType: `code`, correctAnswer: 2, @@ -27,18 +19,10 @@ export const questions = [ question: `What is the difference between \`null\` and \`undefined\`?`, incorrectFeedback: `There are a surprising number of developers who don't know this. Burn the difference into memory because it's extremely important to understand.`, answers: [ - { - text: `\`null\` is a blank value that has to be assigned where \`undefined\` is assigned to any variable/property which hasn't been given a value`, - }, - { - text: `\`null\` is equivalent to \`0\` where \`undefined\` is a falsey value of \`0\``, - }, - { - text: `\`null\` is equivalent to \`false\` when comparing objects and \`undefined\` is equivalent to \`false\` when comparing numbers`, - }, - { - text: `There is no difference`, - }, + `\`null\` is a blank value that has to be assigned where \`undefined\` is assigned to any variable/property which hasn't been given a value`, + `\`null\` is equivalent to 0 where undefined is a falsey value of 0`, + `\`null\` is equivalent to false when comparing objects and undefined is equivalent to \`false\` when comparing numbers`, + `There is no difference`, ], answerType: `markdown`, correctAnswer: 1, @@ -46,18 +30,12 @@ export const questions = [ { id: `variable-pointers`, question: `What does \`y\` equal and why?`, - incorrectFeedback: `This is another thing that trips up developers. The key to remember here is that variables are just *pointers*. When \`x\` gets set to \`3\`, \`y\` is still pointing at the *value* of \`x\` - not \`x\` itself.`, + incorrectFeedback: `This is another thing that trips up developers. The key to remember here is that variables are just *pointers*. When x gets set to 3, y is still pointing at the *value* of x - not x itself.`, codeFigure: `var x = 4;\nvar y = x;\nx = 3;`, answers: [ - { - text: `**3**: Because \`y\` is a reference to \`x\` and \`x\` is \`3\``, - }, - { - text: `**undefined**: Because \`y\` was set to \`x\` but then \`x\` changed so now \`y\` is a dead reference`, - }, - { - text: `**4**: Because \`y\` was set to the value of \`x\`, not \`x\` itself`, - }, + `**3**: Because \`y\` is a reference to \`x\` and \`x\` is 3`, + `**undefined**: Because \`y\` was set to \`x\` but then \`x\` changed so now \`y\` is a dead reference`, + `**4**: Because \`y\` was set to the value of \`x\`, not x itself`, ], answerType: `markdown`, correctAnswer: 3, @@ -68,20 +46,12 @@ export const questions = [ incorrectFeedback: `This is another basic question that you should be able to answer if you're going to be doing Javascript development.`, codeFigure: `function myFunction(){\n\n}`, answers: [ - { - text: `\`(myFunction)\``, - }, - { - text: `\`myFunction||\``, - }, - { - text: `\`myFunction()\``, - }, - { - text: `\`|myFunction|\``, - }, + `(myFunction)`, + `myFunction||`, + `myFunction()`, + `|myFunction|`, ], - answerType: `markdown`, + answerType: `code`, correctAnswer: 3, }, { @@ -89,21 +59,8 @@ export const questions = [ question: `What is \`a\` equal to shown below?`, incorrectFeedback: `It's a common pattern in JS to set variables to functions or other objects. Always keep an eye out to make sure the function isn't being invoked. Every character is important.`, codeFigure: `function myFunction(){\n var x = 5;\n\n return 3 + x;\n}\n\nvar a = myFunction;`, - answers: [ - { - text: `\`error\``, - }, - { - text: `\`myFunction\``, - }, - { - text: `\`undefined\``, - }, - { - text: `\`8\``, - }, - ], - answerType: `markdown`, + answers: [`error`, `myFunction`, `undefined`, `8`], + answerType: `code`, correctAnswer: 2, }, { @@ -111,43 +68,17 @@ export const questions = [ question: `What is \`b\` equal to shown below?`, incorrectFeedback: `Being able to read what the return value of a function is something you should definitely have down pat. Practice writing some basic functions that return simple values - like a number or string.`, codeFigure: `var r = 5;\n\nfunction anotherFunction(){\n return 3 * r;\n}\n\nvar b = anotherFunction();`, - answers: [ - { - text: `\`15\``, - }, - { - text: `\`error\``, - }, - { - text: `\`undefined\``, - }, - { - text: `\`3\``, - }, - ], - answerType: `markdown`, + answers: [`15`, `error`, `undefined`, `3`], + answerType: `code`, correctAnswer: 1, }, { id: `functions-returning-functions`, question: `What is \`c\` equal to shown below?`, - incorrectFeedback: `This is a bit tougher. It can take a bit to read these kind of statements. The way to do it is to work backwards from where the first function is invoked. In this case, start at \`caller(callee)\` and break down what each function does starting from the outside (\`caller\`) and working your way inside (\`callee\`).`, + incorrectFeedback: `This is a bit tougher. It can take a bit to read these kind of statements. The way to do it is to work backwards from where the first function is invoked. In this case, start at caller(callee) and break down what each function does starting from the outside (caller) and working your way inside (callee).`, codeFigure: `var name = "Bill";\n\nfunction caller(func){\n var myObject = {\n name: "Joe"\n };\n\n return func(myObject);\n}\n\nfunction callee(param) {\n return param.name\n}\n\nvar c = caller(callee);`, - answers: [ - { - text: `\`Bill\``, - }, - { - text: `\`caller\``, - }, - { - text: `\`myObject\``, - }, - { - text: `\`Joe\``, - }, - ], - answerType: `markdown`, + answers: [`Bill`, `caller`, `myObject`, `Joe`], + answerType: `code`, correctAnswer: 4, }, { @@ -155,85 +86,38 @@ export const questions = [ question: `What is \`d\` equal to shown below?`, incorrectFeedback: `This is even tougher than the previous one but the concept here is the same. Find where the top level function is invoked and work you're way inward.`, codeFigure: `function caller(f){\n var x = 3;\n\n return f(5)(x);\n}\n\nvar d = caller(function(w){\n var x = 4;\n\n return function(y) {\n return x * y + w;\n };\n});`, - answers: [ - { - text: `\`undefined\``, - }, - { - text: `\`19\``, - }, - { - text: `\`23\``, - }, - { - text: `\`17\``, - }, - ], - answerType: `markdown`, + answers: [`undefined`, `19`, `23`, `17`], + answerType: `code`, correctAnswer: 4, }, { id: `invoking-with-this`, question: `Given this were running in the browser, what would \`e\` be equal to shown below?`, - incorrectFeedback: `Understanding \`this\` is a fundamental piece of Javascript. Check out [this article](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/) for a thorough explanation.`, + incorrectFeedback: `Understanding this is a fundamental piece of Javascript. Check out [this article](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/) for a thorough explanation.`, codeFigure: `var myObject = {\n method: function(){\n return this;\n }\n}\n\nvar e = myObject.method();`, - answers: [ - { - text: `\`undefined\``, - }, - { - text: `\`window\``, - }, - { - text: `\`myObject\``, - }, - { - text: `\`error\``, - }, - ], - answerType: `markdown`, + answers: [`undefined`, `window`, `myObject`, `error`], + answerType: `code`, correctAnswer: 3, }, { id: `window-anonymous-functions`, question: `Given this were running in the browser, what would \`e\` be equal to shown below?`, - incorrectFeedback: `All anonymous functions belong to the global object (in the browser, that's \`window\`). What determines a functions \`this\` (aka "function context"), is how it's *invoked*.`, + incorrectFeedback: `All anonymous functions belong to the global object (in the browser, that's window). What determines a functions this (aka "function context"), is how it's *invoked*.`, codeFigure: `var myObject = {\n method: function(){\n return this;\n }\n}\n\nvar e = myObject.method;\nvar e = x();`, - answers: [ - { - text: `\`undefined\``, - }, - { - text: `\`window\``, - }, - { - text: `\`myObject\``, - }, - { - text: `\`error\``, - }, - ], - answerType: `markdown`, + answers: [`undefined`, `window`, `myObject`, `error`], + answerType: `code`, correctAnswer: 2, }, { id: `function-call-instantiation`, - question: `Which answer below creates a new instance of \`Person\` with an \`age\` property, a \`name\` of \`Dan\`, and assigns that instance to a variable called \`dan\`?`, - incorrectFeedback: `A common pattern, before ES6 \`class\`es, we're to pass objects into a function and assign those props.`, + question: `Which answer below creates a new instance of \`Person\` with an \`age\` property, a \`name\` of Dan, and assigns that instance to a variable called \`dan\`?`, + incorrectFeedback: `A common pattern, before ES6 classes, we're to pass objects into a function and assign those props.`, codeFigure: `function Person(props){\n for(var prop in props) {\n this[prop] = props[prop];\n }\n}`, answers: [ - { - text: `var dan = Person(\n age: 24,\n name: 'Dan'\n);`, - }, - { - text: `var dan = new Person({\n age: 24,\n name: 'Dan'\n});`, - }, - { - text: `var dan = new Person({\n 24,\n 'Dan'\n});`, - }, - { - text: `var dan = Person({\n age: 24,\n name: 'Dan'\n});`, - }, + `var dan = Person(\n age: 24,\n name: 'Dan'\n);`, + `var dan = new Person({\n age: 24,\n name: 'Dan'\n});`, + `var dan = new Person({\n 24,\n 'Dan'\n});`, + `var dan = Person({\n age: 24,\n name: 'Dan'\n});`, ], answerType: `code`, correctAnswer: 2, @@ -243,43 +127,25 @@ export const questions = [ question: `What is the difference between \`var\`, \`let\`, and \`const\``, incorrectFeedback: `This is new syntax for ES6 and is good to know. Read more about it [here](http://wesbos.com/let-vs-const/)`, answers: [ - { - text: `\`let\` is the newer equivalent of \`var\` where \`const\` is used when you want to instantiate a \`new\` constructor function`, - }, - { - text: `Both \`let\` and \`const\` are values that cannot change where \`var\` can change but let is only used for numbers, strings and object variables`, - }, - { - text: `\`let\` and \`const\` are only meant to be used in functions. \`const\` values cannot be changed however. \`var\` is now used for global variables`, - }, - { - text: `\`let\` and \`const\` are both block-scoped variables except that \`const\` cannot be changed and both are newer replacements for \`var\``, - }, + `\`let\` is the newer equivalent of var where \`const\` is used when you want to instantiate a \`new\` constructor function`, + `Both \`let\` and \`const\` are values that cannot change where \`var\` can change but \`let\` is only used for numbers, strings and object variables`, + `\`let\` and \`const\` are only meant to be used in functions. \`const\` values cannot be changed however. \`var\` is now used for global variables`, + `\`let\` and \`const\` are both block-scoped variables except that \`const\` cannot be changed and both are newer replacements for \`var\``, ], answerType: `markdown`, correctAnswer: 4, }, { id: `destructuring-basic`, - question: `Which answer below is an example of two \`const\` variables called \`name\` and \`age\` that were created by destructuring \`myObject\`?`, + question: `Which answer below is an example of two \`const\` variables called \`name\` and \`age\` that were created by destructuring \`myObject?\``, incorrectFeedback: `Destructuring is a really handy feature of ES6 (the latest version of Javascript). Read more about it [here](http://wesbos.com/destructuring-objects/).`, codeFigure: `const myObject = {\n age: 31,\n name: "Dan"\n}`, answers: [ - { - text: `const { age, name } = myObject;`, - }, - { - text: `const age = myObject[age];\nconst name = myObject[name];`, - }, - { - text: `const age, name = myObject;`, - }, - { - text: `const age, name = myObject;`, - }, - { - text: `const myObject[age], myObject[name];`, - }, + `const { age, name } = myObject;`, + `const age = myObject[age];\nconst name = myObject[name];`, + `const age, name = myObject;`, + `const age, name = myObject;`, + `const myObject[age], myObject[name];`, ], answerType: `code`, correctAnswer: 1, @@ -289,15 +155,9 @@ export const questions = [ question: `What are \`import\` and \`export\` used for?`, incorrectFeedback: `These are absolutely critical pieces of the new ES6 syntax that you should know. The MDN site has great documents explaining both [export](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) and [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)`, answers: [ - { - text: `\`import\` is used for passing variables into a function where \`export\` is used for passing variables out of a function`, - }, - { - text: `\`import\` and \`export\` are both used when you want to handle different languages in your application`, - }, - { - text: `\`import\` and \`export\` are used to keep your modules (functions, classes, etc.) in separate files`, - }, + `\`import\` is used for passing variables into a function where \`export\` is used for passing variables out of a function`, + `\`import\` and \`export\` are both used when you want to handle different languages in your application`, + `\`import\` and \`export\` are used to keep your modules (functions, classes, etc.) in separate files`, ], answerType: `markdown`, correctAnswer: 3, @@ -305,39 +165,15 @@ export const questions = [ { id: `class-definition`, question: `What is a \`class\`?`, - incorrectFeedback: `These are absolutely critical pieces of the new ES6 syntax that you should know. The MDN site has great documents explaining both [export](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) and [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)`, + incorrectFeedback: `\`class\`es are used all of the time when working with ES6. Because its just syntacai`, answers: [ - { - text: `\`class\` is the newer form of a constructor function in ES6`, - }, - { - text: `A \`class\` can be used when you want to have an object that is immutable and only houses functions`, - }, - { - text: `\`class\` is similar to object but in ES6, can be used to house more advanced data types like \`async\` and \`yield\``, - }, + `\`class\` is the newer form of a constructor function in ES6`, + `A \`class\` can be used when you want to have an object that is immutable and only houses functions`, + `\`class\` is similar to object but in ES6, can be used to house more advanced data types like \`async\` and \`yield\``, ], answerType: `markdown`, correctAnswer: 1, }, - { - id: `generator-function`, - question: `What is a \`class\`?`, - incorrectFeedback: `ES6 actually didn't introduce anything too fundamentally changing to the language. More "syntax sugar". Since a \`class\` is the future of OOP in Javascript, understanding how it works under the hood is key. There's a great article about them [here](https://css-tricks.com/understanding-javascript-constructors/).`, - answers: [ - { - text: `\`class\` is the newer form of a constructor function in ES6`, - }, - { - text: `A \`class\` can be used when you want to have an object that is immutable and only houses functions`, - }, - { - text: `\`class\` is similar to object but in ES6, can be used to house more advanced data types like \`async\` and \`yield\``, - }, - ], - - correctAnswer: 1, - }, ].map((question, i) => ({ ...question, index: i + 1, diff --git a/src/selectors/quiz.selectors.js b/src/selectors/quiz.selectors.js index d683371..529db80 100644 --- a/src/selectors/quiz.selectors.js +++ b/src/selectors/quiz.selectors.js @@ -6,30 +6,30 @@ const quizSelector = (state) => state.quiz; export const quizStateSelector = createSelector( quizSelector, - (quiz) => quiz.state, + (quiz) => quiz.state ); export const quizSelectedAnswerSelector = createSelector( quizSelector, - (quiz) => quiz.selectedAnswer, + (quiz) => quiz.selectedAnswer ); export const quizAnswersSelector = createSelector( quizSelector, - (quiz) => quiz.answers, + (quiz) => quiz.answers ); export const quizNameSelector = createSelector( quizSelector, - (quiz) => quiz.name, + (quiz) => quiz.name ); export const quizEmailSelector = createSelector( quizSelector, - (quiz) => quiz.email, + (quiz) => quiz.email ); export const quizEmailIsValidSelector = createSelector( quizEmailSelector, - (email) => isValidEmail(email), + (email) => isValidEmail(email) ); diff --git a/src/store/quiz/quiz.reducer.js b/src/store/quiz/quiz.reducer.js index 3484023..2bc6d09 100644 --- a/src/store/quiz/quiz.reducer.js +++ b/src/store/quiz/quiz.reducer.js @@ -11,7 +11,7 @@ import { createReducer } from "helpers"; export default createReducer( { state: `nameCapture`, - selectedAnswer: 1, + selectedAnswer: null, answers: [], email: `dan@dan.com`, name: `Dan`,