mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-03-20 04:01:32 -04:00
feat(challenge-parser): add feedback to mc questions (#51942)
This commit is contained in:
@@ -47,9 +47,14 @@ export type MarkdownRemark = {
|
||||
};
|
||||
};
|
||||
|
||||
export type MultipleChoiceAnswer = {
|
||||
answer: string;
|
||||
feedback: string | null;
|
||||
};
|
||||
|
||||
export type Question = {
|
||||
text: string;
|
||||
answers: string[];
|
||||
answers: MultipleChoiceAnswer[];
|
||||
solution: number;
|
||||
};
|
||||
export type Fields = {
|
||||
|
||||
@@ -231,6 +231,12 @@ class ShowOdin extends Component<ShowOdinProps, ShowOdinState> {
|
||||
const blockNameTitle = `${t(
|
||||
`intro:${superBlock}.blocks.${block}.title`
|
||||
)} - ${title}`;
|
||||
|
||||
const feedback =
|
||||
this.state.selectedOption !== null
|
||||
? answers[this.state.selectedOption].feedback
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Hotkeys
|
||||
executeChallenge={() => {
|
||||
@@ -307,7 +313,7 @@ class ShowOdin extends Component<ShowOdinProps, ShowOdinState> {
|
||||
<h2>{t('learn.question')}</h2>
|
||||
<PrismFormatted className={'line-numbers'} text={text} />
|
||||
<div className='video-quiz-options'>
|
||||
{answers.map((option, index) => (
|
||||
{answers.map(({ answer }, index) => (
|
||||
<label className='video-quiz-option-label' key={index}>
|
||||
<input
|
||||
aria-label={t('aria.answer')}
|
||||
@@ -325,7 +331,7 @@ class ShowOdin extends Component<ShowOdinProps, ShowOdinState> {
|
||||
</span>
|
||||
<PrismFormatted
|
||||
className={'video-quiz-option'}
|
||||
text={option}
|
||||
text={answer}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
@@ -338,7 +344,16 @@ class ShowOdin extends Component<ShowOdinProps, ShowOdinState> {
|
||||
}}
|
||||
>
|
||||
{this.state.isWrongAnswer && (
|
||||
<span>{t('learn.wrong-answer')}</span>
|
||||
<span>
|
||||
{feedback ? (
|
||||
<PrismFormatted
|
||||
className={'multiple-choice-feedback'}
|
||||
text={feedback}
|
||||
/>
|
||||
) : (
|
||||
t('learn.wrong-answer')
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{!this.state.allAssignmentsCompleted &&
|
||||
assignments.length > 0 && (
|
||||
@@ -420,7 +435,10 @@ export const query = graphql`
|
||||
}
|
||||
question {
|
||||
text
|
||||
answers
|
||||
answers {
|
||||
answer
|
||||
feedback
|
||||
}
|
||||
solution
|
||||
}
|
||||
translationPending
|
||||
|
||||
@@ -114,3 +114,7 @@ input:focus-visible + .video-quiz-input-visible {
|
||||
/* remove default prism background */
|
||||
background: none;
|
||||
}
|
||||
|
||||
.multiple-choice-feedback p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,12 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
const blockNameTitle = `${t(
|
||||
`intro:${superBlock}.blocks.${block}.title`
|
||||
)} - ${title}`;
|
||||
|
||||
const feedback =
|
||||
this.state.selectedOption !== null
|
||||
? answers[this.state.selectedOption].feedback
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Hotkeys
|
||||
executeChallenge={() => {
|
||||
@@ -257,7 +263,7 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
<Spacer size='medium' />
|
||||
<ObserveKeys>
|
||||
<div className='video-quiz-options'>
|
||||
{answers.map((option, index) => (
|
||||
{answers.map(({ answer }, index) => (
|
||||
// answers are static and have no natural id property, so
|
||||
// index should be fine as a key:
|
||||
<label
|
||||
@@ -281,7 +287,7 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
</span>
|
||||
<PrismFormatted
|
||||
className={'video-quiz-option'}
|
||||
text={option.replace(/^<p>|<\/p>$/g, '')}
|
||||
text={answer.replace(/^<p>|<\/p>$/g, '')}
|
||||
useSpan
|
||||
noAria
|
||||
/>
|
||||
@@ -296,7 +302,16 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
}}
|
||||
>
|
||||
{this.state.showWrong ? (
|
||||
<span>{t('learn.wrong-answer')}</span>
|
||||
<span>
|
||||
{feedback ? (
|
||||
<PrismFormatted
|
||||
className={'multiple-choice-feedback'}
|
||||
text={feedback}
|
||||
/>
|
||||
) : (
|
||||
t('learn.wrong-answer')
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span>{t('learn.check-answer')}</span>
|
||||
)}
|
||||
@@ -365,7 +380,10 @@ export const query = graphql`
|
||||
}
|
||||
question {
|
||||
text
|
||||
answers
|
||||
answers {
|
||||
answer
|
||||
feedback
|
||||
}
|
||||
solution
|
||||
}
|
||||
translationPending
|
||||
|
||||
@@ -2,6 +2,7 @@ default: true # include all rules, with exceptions below
|
||||
MD002: false # first heading should not be a top level heading
|
||||
MD013: false # lines can be any length
|
||||
MD022: false # headings don't need surrounding by newlines
|
||||
MD024: false # no duplicate headers
|
||||
MD025: false # headings are used as markers by the parser
|
||||
MD031: true # fenced blocks do need surrounding by newlines
|
||||
MD033: false # inline html is required
|
||||
|
||||
@@ -32,10 +32,18 @@ Who should learn to program?
|
||||
|
||||
College students.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
People who want to become software developers.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Everyone.
|
||||
|
||||
@@ -32,10 +32,18 @@ dashedName: introduction-why-program
|
||||
|
||||
大學生。
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
想成爲軟件開發者的人。
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
所有人。
|
||||
|
||||
@@ -32,10 +32,18 @@ dashedName: introduction-why-program
|
||||
|
||||
大学生。
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
想成为软件开发者的人。
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
所有人。
|
||||
|
||||
@@ -32,10 +32,18 @@ Who should learn to program?
|
||||
|
||||
College students.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
People who want to become software developers.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Everyone.
|
||||
|
||||
@@ -32,10 +32,18 @@ Más recursos:
|
||||
|
||||
Estudiantes universitarios.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Personas que quieren convertirse en desarrolladores de software.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Todo el mundo.
|
||||
|
||||
@@ -32,10 +32,18 @@ Wer sollte programmieren lernen?
|
||||
|
||||
College Studenten.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Menschen, die Softwareentwickler werden wollen.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Jeder.
|
||||
|
||||
@@ -32,10 +32,18 @@ Chi dovrebbe imparare a programmare?
|
||||
|
||||
Studenti universitari.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Persone che vogliono diventare sviluppatori di software.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Tutti.
|
||||
|
||||
@@ -32,10 +32,18 @@ dashedName: introduction-why-program
|
||||
|
||||
大学生
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
ソフトウェアの開発者になりたい人
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
すべての人
|
||||
|
||||
@@ -32,10 +32,18 @@ Quem deveria aprender a programar?
|
||||
|
||||
Estudantes universitários.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Pessoas que querem se tornar desenvolvedores de software.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Todos.
|
||||
|
||||
@@ -32,10 +32,18 @@ Who should learn to program?
|
||||
|
||||
College students.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
People who want to become software developers.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Everyone.
|
||||
|
||||
@@ -32,10 +32,18 @@ dashedName: introduction-why-program
|
||||
|
||||
Студентам коледжу.
|
||||
|
||||
### --feedback--
|
||||
|
||||
College students should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Людям, які хочуть стати розробниками програмного забезпечення.
|
||||
|
||||
### --feedback--
|
||||
|
||||
People who want to become software developers should learn to program, but there's a better answer.
|
||||
|
||||
---
|
||||
|
||||
Всім.
|
||||
|
||||
@@ -94,7 +94,14 @@ const schema = Joi.object()
|
||||
}),
|
||||
question: Joi.object().keys({
|
||||
text: Joi.string().required(),
|
||||
answers: Joi.array().items(Joi.string()).required(),
|
||||
answers: Joi.array()
|
||||
.items(
|
||||
Joi.object().keys({
|
||||
answer: Joi.string().required(),
|
||||
feedback: Joi.string().allow(null)
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
solution: Joi.number().required()
|
||||
}),
|
||||
required: Joi.array().items(
|
||||
|
||||
@@ -136,6 +136,14 @@ Solutions are used for the CI tests to ensure that changes to the hints will sti
|
||||
// third solution etc. - Your solutions should be in HTML.
|
||||
```
|
||||
|
||||
# --assignments--
|
||||
|
||||
This will show a checkbox that campers have to check before completing a challenge
|
||||
|
||||
---
|
||||
|
||||
This will show another checkbox that campers have to check before completing a challenge
|
||||
|
||||
# --question--
|
||||
|
||||
These fields are currently used for the multiple-choice Python challenges.
|
||||
@@ -148,6 +156,10 @@ The question text goes here.
|
||||
|
||||
Answer 1
|
||||
|
||||
### --feedback--
|
||||
|
||||
This will be shown as feedback when campers guess this answer
|
||||
|
||||
---
|
||||
|
||||
Answer 2
|
||||
|
||||
47
pnpm-lock.yaml
generated
47
pnpm-lock.yaml
generated
@@ -1235,6 +1235,9 @@ importers:
|
||||
unist-util-find-all-after:
|
||||
specifier: 3.0.2
|
||||
version: 3.0.2
|
||||
unist-util-find-all-before:
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1
|
||||
unist-util-find-all-between:
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
@@ -1655,6 +1658,7 @@ packages:
|
||||
|
||||
/@aws-crypto/crc32@3.0.0:
|
||||
resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@aws-crypto/util': 3.0.0
|
||||
'@aws-sdk/types': 3.433.0
|
||||
@@ -8311,6 +8315,7 @@ packages:
|
||||
|
||||
/@smithy/eventstream-codec@2.0.10:
|
||||
resolution: {integrity: sha512-3SSDgX2nIsFwif6m+I4+ar4KDcZX463Noes8ekBgQHitULiWvaDZX8XqPaRQSQ4bl1vbeVXHklJfv66MnVO+lw==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@aws-crypto/crc32': 3.0.0
|
||||
'@smithy/types': 2.4.0
|
||||
@@ -8381,6 +8386,7 @@ packages:
|
||||
/@smithy/is-array-buffer@2.0.0:
|
||||
resolution: {integrity: sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
@@ -15935,6 +15941,7 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
supports-color: 8.1.1
|
||||
dev: true
|
||||
|
||||
/debug@4.3.1:
|
||||
resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==}
|
||||
@@ -16270,7 +16277,7 @@ packages:
|
||||
'@types/tmp': 0.0.33
|
||||
application-config-path: 0.1.1
|
||||
command-exists: 1.2.9
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
eol: 0.9.1
|
||||
get-port: 3.2.0
|
||||
glob: 7.2.3
|
||||
@@ -17130,7 +17137,7 @@ packages:
|
||||
/eslint-import-resolver-node@0.3.9:
|
||||
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
is-core-module: 2.13.1
|
||||
resolve: 1.22.6
|
||||
transitivePeerDependencies:
|
||||
@@ -17181,7 +17188,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.9.5)
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
eslint: 7.32.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.29.0)(eslint@8.52.0)
|
||||
@@ -17210,7 +17217,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.62.0(eslint@8.52.0)(typescript@4.9.5)
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
eslint: 8.52.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.29.0)(eslint@8.52.0)
|
||||
@@ -17270,7 +17277,7 @@ packages:
|
||||
array.prototype.findlastindex: 1.2.3
|
||||
array.prototype.flat: 1.3.2
|
||||
array.prototype.flatmap: 1.3.2
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
doctrine: 2.1.0
|
||||
eslint: 7.32.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
@@ -17304,7 +17311,7 @@ packages:
|
||||
array.prototype.findlastindex: 1.2.3
|
||||
array.prototype.flat: 1.3.2
|
||||
array.prototype.flatmap: 1.3.2
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.52.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
@@ -18548,7 +18555,7 @@ packages:
|
||||
debug:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
|
||||
/follow-redirects@1.15.3(debug@4.3.4):
|
||||
resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==}
|
||||
@@ -19396,7 +19403,7 @@ packages:
|
||||
css-minimizer-webpack-plugin: 2.0.0(webpack@5.89.0)
|
||||
css.escape: 1.5.1
|
||||
date-fns: 2.30.0
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
deepmerge: 4.3.1
|
||||
del: 5.1.0
|
||||
detect-port: 1.5.1
|
||||
@@ -23053,7 +23060,7 @@ packages:
|
||||
dependencies:
|
||||
async: 0.9.2
|
||||
commondir: 1.0.1
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
lodash: 4.17.21
|
||||
semver: 5.7.2
|
||||
strong-globalize: 4.1.3
|
||||
@@ -23066,7 +23073,7 @@ packages:
|
||||
resolution: {integrity: sha512-vDRR4gqkvGOEXh5yL383xGuGxUW9xtF+NCY6/lJu1VAgupKltZxEx3Vw+L3nsGvQrlkJTSmiK3jk72qxkoBtbw==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
lodash: 4.17.21
|
||||
loopback-swagger: 5.9.0
|
||||
strong-globalize: 4.1.3
|
||||
@@ -23081,7 +23088,7 @@ packages:
|
||||
dependencies:
|
||||
async: 2.6.4
|
||||
bson: 1.1.6
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
loopback-connector: 4.11.1
|
||||
mongodb: 3.6.9
|
||||
strong-globalize: 4.1.3
|
||||
@@ -23125,7 +23132,7 @@ packages:
|
||||
dependencies:
|
||||
async: 2.6.4
|
||||
bluebird: 3.7.2
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
depd: 1.1.2
|
||||
inflection: 1.13.4
|
||||
lodash: 4.17.21
|
||||
@@ -23149,7 +23156,7 @@ packages:
|
||||
resolution: {integrity: sha512-p0qSzuuX7eATe5Bxy+RqCj3vSfSFfdCtqyf3yuC+DpchMvgal33XlhEi2UmywyK/Ym28oVnZxxWmfrwFMzSwLQ==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
@@ -23159,7 +23166,7 @@ packages:
|
||||
engines: {node: '>=8.9'}
|
||||
dependencies:
|
||||
async: 2.6.4
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
strong-globalize: 4.1.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -23170,7 +23177,7 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
async: 2.6.4
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
ejs: 2.7.4
|
||||
lodash: 4.17.21
|
||||
strong-globalize: 4.1.3
|
||||
@@ -31233,6 +31240,12 @@ packages:
|
||||
unist-util-is: 4.1.0
|
||||
dev: false
|
||||
|
||||
/unist-util-find-all-before@3.0.1:
|
||||
resolution: {integrity: sha512-00YMzXOOu4z/qCHKQI6z7TeERAbIBda0E/1sZqP0cJdGd3d1OtUPvQlMhNCwz4QBFo//GcW2dHOYXPdu3jINdA==}
|
||||
dependencies:
|
||||
unist-util-is: 4.1.0
|
||||
dev: false
|
||||
|
||||
/unist-util-find-all-between@2.1.0:
|
||||
resolution: {integrity: sha512-OCCUtDD8UHKeODw3TPXyFDxPCbpgBzbGTTaDpR68nvxkwiVcawBqMVrokfBMvUi7ij2F5q7S4s4Jq5dvkcBt+w==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -32045,7 +32058,7 @@ packages:
|
||||
/webpack-virtual-modules@0.2.2:
|
||||
resolution: {integrity: sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==}
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -32053,7 +32066,7 @@ packages:
|
||||
/webpack-virtual-modules@0.3.2:
|
||||
resolution: {integrity: sha512-RXQXioY6MhzM4CNQwmBwKXYgBs6ulaiQ8bkNQEl2J6Z+V+s7lgl/wGvaI/I0dLnYKB8cKsxQc17QOAVIphPLDw==}
|
||||
dependencies:
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
debug: 3.2.7(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"unist-util-find": "1.0.2",
|
||||
"unist-util-find-after": "3.0.0",
|
||||
"unist-util-find-all-after": "3.0.2",
|
||||
"unist-util-find-all-before": "3.0.1",
|
||||
"unist-util-find-all-between": "2.1.0",
|
||||
"unist-util-is": "4.1.0",
|
||||
"unist-util-modify-children": "2.0.0",
|
||||
|
||||
@@ -219,6 +219,45 @@
|
||||
"indent": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 31, "column": 5, "offset": 231 },
|
||||
"end": { "line": 31, "column": 17, "offset": 243 },
|
||||
"indent": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 31, "column": 1, "offset": 227 },
|
||||
"end": { "line": 31, "column": 17, "offset": 243 },
|
||||
"indent": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "That is not correct.",
|
||||
"position": {
|
||||
"start": { "line": 33, "column": 1, "offset": 245 },
|
||||
"end": { "line": 33, "column": 21, "offset": 265 },
|
||||
"indent": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 33, "column": 1, "offset": 245 },
|
||||
"end": { "line": 33, "column": 21, "offset": 265 },
|
||||
"indent": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "thematicBreak",
|
||||
"position": {
|
||||
|
||||
@@ -28,6 +28,10 @@ Question line 1
|
||||
|
||||
Some inline `code`
|
||||
|
||||
### --feedback--
|
||||
|
||||
That is not correct.
|
||||
|
||||
---
|
||||
|
||||
Some *italics*
|
||||
|
||||
@@ -33,10 +33,19 @@ exports[`challenge parser it should parse video questions 1`] = `
|
||||
</section>",
|
||||
"question": {
|
||||
"answers": [
|
||||
"<p>Some inline <code>code</code></p>",
|
||||
"<p>Some <em>italics</em></p>
|
||||
{
|
||||
"answer": "<p>Some inline <code>code</code></p>",
|
||||
"feedback": "<p>That is not correct.</p>",
|
||||
},
|
||||
{
|
||||
"answer": "<p>Some <em>italics</em></p>
|
||||
<p>A second answer paragraph.</p>",
|
||||
"<p><code> code in </code> code tags</p>",
|
||||
"feedback": null,
|
||||
},
|
||||
{
|
||||
"answer": "<p><code> code in </code> code tags</p>",
|
||||
"feedback": null,
|
||||
},
|
||||
],
|
||||
"solution": 3,
|
||||
"text": "<p>Question line 1</p>
|
||||
|
||||
@@ -4,10 +4,19 @@ exports[`add-video-question plugin should match the video snapshot 1`] = `
|
||||
{
|
||||
"question": {
|
||||
"answers": [
|
||||
"<p>Some inline <code>code</code></p>",
|
||||
"<p>Some <em>italics</em></p>
|
||||
{
|
||||
"answer": "<p>Some inline <code>code</code></p>",
|
||||
"feedback": "<p>That is not correct.</p>",
|
||||
},
|
||||
{
|
||||
"answer": "<p>Some <em>italics</em></p>
|
||||
<p>A second answer paragraph.</p>",
|
||||
"<p><code> code in </code> code tags</p>",
|
||||
"feedback": null,
|
||||
},
|
||||
{
|
||||
"answer": "<p><code> code in </code> code tags</p>",
|
||||
"feedback": null,
|
||||
},
|
||||
],
|
||||
"solution": 3,
|
||||
"text": "<p>Question line 1</p>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const { root } = require('mdast-builder');
|
||||
const find = require('unist-util-find');
|
||||
const getAllBetween = require('./utils/between-headings');
|
||||
const getAllBefore = require('./utils/before-heading');
|
||||
const mdastToHtml = require('./utils/mdast-to-html');
|
||||
|
||||
const { splitOnThematicBreak } = require('./utils/split-on-thematic-break');
|
||||
@@ -36,7 +38,27 @@ function getQuestion(textNodes, answersNodes, solutionNodes) {
|
||||
|
||||
function getAnswers(answersNodes) {
|
||||
const answerGroups = splitOnThematicBreak(answersNodes);
|
||||
return answerGroups.map(answer => mdastToHtml(answer));
|
||||
|
||||
return answerGroups.map(answerGroup => {
|
||||
const answerTree = root(answerGroup);
|
||||
const feedback = find(answerTree, { value: '--feedback--' });
|
||||
|
||||
if (feedback) {
|
||||
const answerNodes = getAllBefore(answerTree, '--feedback--');
|
||||
const feedbackNodes = getAllBetween(answerTree, '--feedback--');
|
||||
|
||||
if (answerNodes.length < 1) {
|
||||
throw Error('Answer missing');
|
||||
}
|
||||
|
||||
return {
|
||||
answer: mdastToHtml(answerNodes),
|
||||
feedback: mdastToHtml(feedbackNodes)
|
||||
};
|
||||
}
|
||||
|
||||
return { answer: mdastToHtml(answerGroup), feedback: null };
|
||||
});
|
||||
}
|
||||
|
||||
function getSolution(solutionNodes) {
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('add-video-question plugin', () => {
|
||||
});
|
||||
|
||||
it('should generate a question object from a video challenge AST', () => {
|
||||
expect.assertions(8);
|
||||
expect.assertions(10);
|
||||
plugin(mockVideoAST, file);
|
||||
const testObject = file.data.question;
|
||||
expect(Object.keys(testObject).length).toBe(3);
|
||||
@@ -33,7 +33,9 @@ describe('add-video-question plugin', () => {
|
||||
expect(typeof testObject.solution).toBe('number');
|
||||
expect(testObject).toHaveProperty('answers');
|
||||
expect(Array.isArray(testObject.answers)).toBe(true);
|
||||
expect(typeof testObject.answers[0]).toBe('string');
|
||||
expect(typeof testObject.answers[0]).toBe('object');
|
||||
expect(testObject.answers[0]).toHaveProperty('answer');
|
||||
expect(testObject.answers[0]).toHaveProperty('feedback');
|
||||
});
|
||||
|
||||
it('should convert question and answer markdown into html', () => {
|
||||
@@ -46,12 +48,19 @@ describe('add-video-question plugin', () => {
|
||||
'</code></pre>'
|
||||
);
|
||||
expect(testObject.solution).toBe(3);
|
||||
expect(testObject.answers[0]).toBe('<p>Some inline <code>code</code></p>');
|
||||
expect(testObject.answers[1]).toBe(`<p>Some <em>italics</em></p>
|
||||
<p>A second answer paragraph.</p>`);
|
||||
expect(testObject.answers[2]).toBe(
|
||||
'<p><code> code in </code> code tags</p>'
|
||||
);
|
||||
expect(testObject.answers[0]).toStrictEqual({
|
||||
answer: '<p>Some inline <code>code</code></p>',
|
||||
feedback: '<p>That is not correct.</p>'
|
||||
});
|
||||
expect(testObject.answers[1]).toStrictEqual({
|
||||
answer: `<p>Some <em>italics</em></p>
|
||||
<p>A second answer paragraph.</p>`,
|
||||
feedback: null
|
||||
});
|
||||
expect(testObject.answers[2]).toStrictEqual({
|
||||
answer: '<p><code> code in </code> code tags</p>',
|
||||
feedback: null
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: consider testing for more specific messages. Ideally we them to say
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const find = require('unist-util-find');
|
||||
const findAllBefore = require('unist-util-find-all-before');
|
||||
|
||||
function getAllBefore(tree, marker) {
|
||||
const start = find(tree, {
|
||||
type: 'heading',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: marker
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!start) return [];
|
||||
|
||||
// reverse because it goes up the tree backwards and adds nodes in that order
|
||||
return findAllBefore(tree, start).reverse();
|
||||
}
|
||||
|
||||
module.exports = getAllBefore;
|
||||
@@ -0,0 +1,24 @@
|
||||
const isArray = require('lodash/isArray');
|
||||
const simpleAst = require('../../__fixtures__/ast-simple.json');
|
||||
const getAllBefore = require('./before-heading');
|
||||
|
||||
describe('before-headings', () => {
|
||||
it('should return an array', () => {
|
||||
expect.assertions(1);
|
||||
const actual = getAllBefore(simpleAst, '--hints--');
|
||||
expect(isArray(actual)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an empty array if the marker is not present', () => {
|
||||
expect.assertions(2);
|
||||
const actual = getAllBefore(simpleAst, '--not-a-marker--');
|
||||
expect(isArray(actual)).toBe(true);
|
||||
expect(actual.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should include the whole AST before the marker', () => {
|
||||
expect.assertions(1);
|
||||
const actual = getAllBefore(simpleAst, '--hints--');
|
||||
expect(actual).toHaveLength(6);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user