mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: nicer formatting for fill in the blank challenges (#52552)
This commit is contained in:
committed by
GitHub
parent
0673847088
commit
9fe6a46b81
@@ -0,0 +1,130 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { parseBlanks } from './parse-blanks';
|
||||
|
||||
describe('parseBlanks', () => {
|
||||
it('handles strings without blanks', () => {
|
||||
expect(parseBlanks('<p>hello world</p>')).toEqual([
|
||||
[{ type: 'text', value: 'hello world' }]
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles strings without blanks and with multiple paragraphs', () => {
|
||||
expect(parseBlanks('<p>hello world</p><p>dlrow olleh</p>')).toEqual([
|
||||
[{ type: 'text', value: 'hello world' }],
|
||||
[{ type: 'text', value: 'dlrow olleh' }]
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles strings with blanks', () => {
|
||||
expect(parseBlanks('<p>hello _!</p>')).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '!' }
|
||||
]
|
||||
]);
|
||||
|
||||
expect(parseBlanks('<p>hello _! Nice _ to meet you.</p>')).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '! Nice ' },
|
||||
{ type: 'blank', value: 1 },
|
||||
{ type: 'text', value: ' to meet you.' }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles paragraphs with a leading blank', () => {
|
||||
expect(parseBlanks('<p>_ hello</p>')).toEqual([
|
||||
[
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: ' hello' }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('removes trailing empty strings', () => {
|
||||
expect(parseBlanks('<p>hello _</p>')).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles strings with blanks and multiple paragraphs', () => {
|
||||
expect(parseBlanks(`<p>hello _!</p><p>dlrow _</p>`)).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '!' }
|
||||
],
|
||||
[
|
||||
{ type: 'text', value: 'dlrow ' },
|
||||
{ type: 'blank', value: 1 }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores newlines between paragraphs', () => {
|
||||
expect(
|
||||
parseBlanks(`<p>hello _!</p>
|
||||
|
||||
<p>dlrow _</p>`)
|
||||
).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '!' }
|
||||
],
|
||||
[
|
||||
{ type: 'text', value: 'dlrow ' },
|
||||
{ type: 'blank', value: 1 }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores whitespace surrounding the input', () => {
|
||||
expect(
|
||||
parseBlanks(` <p>hello _!</p>
|
||||
`)
|
||||
).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '!' }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('counts blanks across multiple paragraphs', () => {
|
||||
expect(
|
||||
parseBlanks(`<p>hello _!</p><p>dlrow _ _</p><p> even _ more _</p>`)
|
||||
).toEqual([
|
||||
[
|
||||
{ type: 'text', value: 'hello ' },
|
||||
{ type: 'blank', value: 0 },
|
||||
{ type: 'text', value: '!' }
|
||||
],
|
||||
[
|
||||
{ type: 'text', value: 'dlrow ' },
|
||||
{ type: 'blank', value: 1 },
|
||||
{ type: 'text', value: ' ' },
|
||||
{ type: 'blank', value: 2 }
|
||||
],
|
||||
[
|
||||
{ type: 'text', value: ' even ' },
|
||||
{ type: 'blank', value: 3 },
|
||||
{ type: 'text', value: ' more ' },
|
||||
{ type: 'blank', value: 4 }
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it('throws if the string is not wrapped in p tags', () => {
|
||||
expect(() => parseBlanks('hello _!')).toThrow();
|
||||
expect(() => parseBlanks('<p>hello _!</p>hello _!')).toThrow();
|
||||
expect(() => parseBlanks('hello _!<p>hello</p>')).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
type TextNode = { type: 'text'; value: string };
|
||||
type BlankNode = { type: 'blank'; value: number };
|
||||
type ParagraphElement = TextNode | BlankNode;
|
||||
|
||||
export const parseBlanks = (text: string) => {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed.startsWith('<p>') || !trimmed.endsWith('</p>')) {
|
||||
throw new Error(`Expected
|
||||
${text}
|
||||
to be wrapped in <p> tags`);
|
||||
}
|
||||
// We're expecting only paragraphs, so text after </p> and before <p> can be
|
||||
// ignored.
|
||||
const rawParagraphs = trimmed
|
||||
.split('<p>')
|
||||
.map(s => s.replace(/<\/p>\s*/, ''));
|
||||
// There is always a leading empty string, so we remove it.
|
||||
rawParagraphs.shift();
|
||||
|
||||
const { paragraphs } = rawParagraphs.reduce(
|
||||
(acc, p) => {
|
||||
const splitByBlank = p.split('_');
|
||||
|
||||
const parsedParagraph = splitByBlank
|
||||
.map<ParagraphElement[]>((text, i) => [
|
||||
{ type: 'text', value: text },
|
||||
{ type: 'blank', value: acc.count + i }
|
||||
])
|
||||
.flat();
|
||||
parsedParagraph.pop(); // remove last blank
|
||||
|
||||
const paragraph = parsedParagraph.filter(p => {
|
||||
// remove empty strings
|
||||
if (p.type === 'text') {
|
||||
return p.value;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return {
|
||||
count: acc.count + splitByBlank.length - 1,
|
||||
paragraphs: [...acc.paragraphs, paragraph]
|
||||
};
|
||||
},
|
||||
{ count: 0, paragraphs: [] } as {
|
||||
count: number;
|
||||
paragraphs: ParagraphElement[][];
|
||||
}
|
||||
);
|
||||
|
||||
return paragraphs;
|
||||
};
|
||||
@@ -27,12 +27,13 @@ import {
|
||||
openModal,
|
||||
updateSolutionFormValues
|
||||
} from '../redux/actions';
|
||||
import Scene from '../components/scene/scene';
|
||||
import { isChallengeCompletedSelector } from '../redux/selectors';
|
||||
import { parseBlanks } from './parse-blanks';
|
||||
|
||||
// Styles
|
||||
import '../video.css';
|
||||
import './show.css';
|
||||
import Scene from '../components/scene/scene';
|
||||
|
||||
// Redux Setup
|
||||
const mapStateToProps = createSelector(
|
||||
@@ -273,8 +274,8 @@ class ShowFillInTheBlank extends Component<
|
||||
)} - ${title}`;
|
||||
|
||||
const { allBlanksFilled, feedback, showFeedback, showWrong } = this.state;
|
||||
const paragraphs = parseBlanks(sentence);
|
||||
|
||||
const splitSentence = sentence.replace(/^<p>|<\/p>$/g, '').split('_');
|
||||
const blankAnswers = blanks.map(b => b.answer);
|
||||
|
||||
return (
|
||||
@@ -328,37 +329,37 @@ class ShowFillInTheBlank extends Component<
|
||||
if it encounters a key combination, so we have to pass in the individual keys to observe */}
|
||||
<ObserveKeys only={['ctrl', 'cmd', 'enter']}>
|
||||
<div>
|
||||
<p>
|
||||
{splitSentence.map((s, i) => {
|
||||
return (
|
||||
<Fragment key={i}>
|
||||
<PrismFormatted
|
||||
text={this.addCodeTags(s, i, blankAnswers.length)}
|
||||
className={`code-tag ${this.addPrismClass(
|
||||
i,
|
||||
blankAnswers.length
|
||||
)}`}
|
||||
useSpan
|
||||
noAria
|
||||
/>
|
||||
{blankAnswers[i] && (
|
||||
<input
|
||||
type='text'
|
||||
maxLength={blankAnswers[i].length + 3}
|
||||
className={`fill-in-the-blank-input ${this.addInputClass(
|
||||
i
|
||||
)}`}
|
||||
onChange={this.handleInputChange}
|
||||
data-index={i}
|
||||
style={{
|
||||
width: `${blankAnswers[i].length * 11 + 11}px`
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
{paragraphs.map((p, i) => {
|
||||
return (
|
||||
// both keys, i and j, are stable between renders, since
|
||||
// the paragraphs are static.
|
||||
<p key={i}>
|
||||
{p.map((node, j) => {
|
||||
if (node.type === 'text') return node.value;
|
||||
if (node.type === 'blank')
|
||||
return (
|
||||
<input
|
||||
key={j}
|
||||
type='text'
|
||||
maxLength={
|
||||
blankAnswers[node.value].length + 3
|
||||
}
|
||||
className={`fill-in-the-blank-input ${this.addInputClass(
|
||||
node.value
|
||||
)}`}
|
||||
onChange={this.handleInputChange}
|
||||
data-index={node.value}
|
||||
style={{
|
||||
width: `${
|
||||
blankAnswers[node.value].length * 11 + 11
|
||||
}px`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</ObserveKeys>
|
||||
<Spacer size='medium' />
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
{
|
||||
"type": "root",
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--description--",
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 3, "offset": 2 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 92, "offset": 110 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "You are a programmer, right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 92, "offset": 110 },
|
||||
"end": { "line": 3, "column": 122, "offset": 140 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " Here, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 122, "offset": 140 },
|
||||
"end": { "line": 3, "column": 129, "offset": 147 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 129, "offset": 147 },
|
||||
"end": { "line": 3, "column": 137, "offset": 155 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " is used as a tag to check or confirm the previous statement.",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 137, "offset": 155 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--fillInTheBlank--",
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 3, "offset": 220 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 1, "offset": 218 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--sentence--",
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 4, "offset": 243 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 1, "offset": 240 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "A sentence _ paragraph 1",
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 9, "column": 27, "offset": 283 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": "\n",
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 27, "offset": 283 },
|
||||
"end": { "line": 10, "column": 1, "offset": 284 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "not enough newlines, so no paragraph 2",
|
||||
"position": {
|
||||
"start": { "line": 10, "column": 1, "offset": 284 },
|
||||
"end": { "line": 10, "column": 41, "offset": 324 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 10, "column": 41, "offset": 324 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Sentence in _ 2",
|
||||
"position": {
|
||||
"start": { "line": 12, "column": 1, "offset": 326 },
|
||||
"end": { "line": 12, "column": 16, "offset": 341 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 12, "column": 1, "offset": 326 },
|
||||
"end": { "line": 12, "column": 16, "offset": 341 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--blanks--",
|
||||
"position": {
|
||||
"start": { "line": 14, "column": 4, "offset": 346 },
|
||||
"end": { "line": 14, "column": 14, "offset": 356 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 14, "column": 1, "offset": 343 },
|
||||
"end": { "line": 14, "column": 14, "offset": 356 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "are",
|
||||
"position": {
|
||||
"start": { "line": 16, "column": 1, "offset": 358 },
|
||||
"end": { "line": 16, "column": 6, "offset": 363 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 16, "column": 1, "offset": 358 },
|
||||
"end": { "line": 16, "column": 6, "offset": 363 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 18, "column": 5, "offset": 369 },
|
||||
"end": { "line": 18, "column": 17, "offset": 381 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 18, "column": 1, "offset": 365 },
|
||||
"end": { "line": 18, "column": 17, "offset": 381 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 20, "column": 1, "offset": 383 },
|
||||
"end": { "line": 20, "column": 43, "offset": 425 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 20, "column": 1, "offset": 383 },
|
||||
"end": { "line": 20, "column": 43, "offset": 425 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "thematicBreak",
|
||||
"position": {
|
||||
"start": { "line": 22, "column": 1, "offset": 427 },
|
||||
"end": { "line": 22, "column": 4, "offset": 430 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right",
|
||||
"position": {
|
||||
"start": { "line": 24, "column": 1, "offset": 432 },
|
||||
"end": { "line": 24, "column": 8, "offset": 439 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 24, "column": 1, "offset": 432 },
|
||||
"end": { "line": 24, "column": 8, "offset": 439 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 26, "column": 5, "offset": 445 },
|
||||
"end": { "line": 26, "column": 17, "offset": 457 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 26, "column": 1, "offset": 441 },
|
||||
"end": { "line": 26, "column": 17, "offset": 457 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 28, "column": 1, "offset": 459 },
|
||||
"end": { "line": 28, "column": 43, "offset": 501 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 28, "column": 1, "offset": 459 },
|
||||
"end": { "line": 28, "column": 43, "offset": 501 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 29, "column": 1, "offset": 502 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
{
|
||||
"type": "root",
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--description--",
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 3, "offset": 2 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 92, "offset": 110 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "You are a programmer, right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 92, "offset": 110 },
|
||||
"end": { "line": 3, "column": 122, "offset": 140 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " Here, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 122, "offset": 140 },
|
||||
"end": { "line": 3, "column": 129, "offset": 147 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 129, "offset": 147 },
|
||||
"end": { "line": 3, "column": 137, "offset": 155 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " is used as a tag to check or confirm the previous statement.",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 137, "offset": 155 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--fillInTheBlank--",
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 3, "offset": 220 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 1, "offset": 218 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--sentence--",
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 4, "offset": 243 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 1, "offset": 240 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "A sentence _ paragraph 1",
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 9, "column": 27, "offset": 283 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 9, "column": 27, "offset": 283 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Sentence in _ 2",
|
||||
"position": {
|
||||
"start": { "line": 11, "column": 1, "offset": 285 },
|
||||
"end": { "line": 11, "column": 16, "offset": 300 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 11, "column": 1, "offset": 285 },
|
||||
"end": { "line": 11, "column": 16, "offset": 300 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--blanks--",
|
||||
"position": {
|
||||
"start": { "line": 13, "column": 4, "offset": 305 },
|
||||
"end": { "line": 13, "column": 14, "offset": 315 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 13, "column": 1, "offset": 302 },
|
||||
"end": { "line": 13, "column": 14, "offset": 315 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "are",
|
||||
"position": {
|
||||
"start": { "line": 15, "column": 1, "offset": 317 },
|
||||
"end": { "line": 15, "column": 6, "offset": 322 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 15, "column": 1, "offset": 317 },
|
||||
"end": { "line": 15, "column": 6, "offset": 322 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 17, "column": 5, "offset": 328 },
|
||||
"end": { "line": 17, "column": 17, "offset": 340 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 17, "column": 1, "offset": 324 },
|
||||
"end": { "line": 17, "column": 17, "offset": 340 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 19, "column": 1, "offset": 342 },
|
||||
"end": { "line": 19, "column": 43, "offset": 384 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 19, "column": 1, "offset": 342 },
|
||||
"end": { "line": 19, "column": 43, "offset": 384 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "thematicBreak",
|
||||
"position": {
|
||||
"start": { "line": 21, "column": 1, "offset": 386 },
|
||||
"end": { "line": 21, "column": 4, "offset": 389 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right",
|
||||
"position": {
|
||||
"start": { "line": 23, "column": 1, "offset": 391 },
|
||||
"end": { "line": 23, "column": 8, "offset": 398 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 23, "column": 1, "offset": 391 },
|
||||
"end": { "line": 23, "column": 8, "offset": 398 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 25, "column": 5, "offset": 404 },
|
||||
"end": { "line": 25, "column": 17, "offset": 416 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 25, "column": 1, "offset": 400 },
|
||||
"end": { "line": 25, "column": 17, "offset": 416 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 27, "column": 1, "offset": 418 },
|
||||
"end": { "line": 27, "column": 43, "offset": 460 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 27, "column": 1, "offset": 418 },
|
||||
"end": { "line": 27, "column": 43, "offset": 460 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 28, "column": 1, "offset": 461 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
{
|
||||
"type": "root",
|
||||
"children": [
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--description--",
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 3, "offset": 2 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 1, "column": 18, "offset": 17 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 92, "offset": 110 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "You are a programmer, right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 92, "offset": 110 },
|
||||
"end": { "line": 3, "column": 122, "offset": 140 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " Here, ",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 122, "offset": 140 },
|
||||
"end": { "line": 3, "column": 129, "offset": 147 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right?",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 129, "offset": 147 },
|
||||
"end": { "line": 3, "column": 137, "offset": 155 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"value": " is used as a tag to check or confirm the previous statement.",
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 137, "offset": 155 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 3, "column": 1, "offset": 19 },
|
||||
"end": { "line": 3, "column": 198, "offset": 216 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 1,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--fillInTheBlank--",
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 3, "offset": 220 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 5, "column": 1, "offset": 218 },
|
||||
"end": { "line": 5, "column": 21, "offset": 238 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--sentence--",
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 4, "offset": 243 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 7, "column": 1, "offset": 240 },
|
||||
"end": { "line": 7, "column": 16, "offset": 255 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "A sentence _ paragraph 1",
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 9, "column": 27, "offset": 283 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 9, "column": 1, "offset": 257 },
|
||||
"end": { "line": 9, "column": 27, "offset": 283 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "Sentence in _ 2",
|
||||
"position": {
|
||||
"start": { "line": 11, "column": 1, "offset": 285 },
|
||||
"end": { "line": 11, "column": 18, "offset": 302 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 11, "column": 1, "offset": 285 },
|
||||
"end": { "line": 11, "column": 18, "offset": 302 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 2,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--blanks--",
|
||||
"position": {
|
||||
"start": { "line": 13, "column": 4, "offset": 307 },
|
||||
"end": { "line": 13, "column": 14, "offset": 317 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 13, "column": 1, "offset": 304 },
|
||||
"end": { "line": 13, "column": 14, "offset": 317 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "are",
|
||||
"position": {
|
||||
"start": { "line": 15, "column": 1, "offset": 319 },
|
||||
"end": { "line": 15, "column": 6, "offset": 324 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 15, "column": 1, "offset": 319 },
|
||||
"end": { "line": 15, "column": 6, "offset": 324 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 17, "column": 5, "offset": 330 },
|
||||
"end": { "line": 17, "column": 17, "offset": 342 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 17, "column": 1, "offset": 326 },
|
||||
"end": { "line": 17, "column": 17, "offset": 342 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 19, "column": 1, "offset": 344 },
|
||||
"end": { "line": 19, "column": 43, "offset": 386 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 19, "column": 1, "offset": 344 },
|
||||
"end": { "line": 19, "column": 43, "offset": 386 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "thematicBreak",
|
||||
"position": {
|
||||
"start": { "line": 21, "column": 1, "offset": 388 },
|
||||
"end": { "line": 21, "column": 4, "offset": 391 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "inlineCode",
|
||||
"value": "right",
|
||||
"position": {
|
||||
"start": { "line": 23, "column": 1, "offset": 393 },
|
||||
"end": { "line": 23, "column": 8, "offset": 400 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 23, "column": 1, "offset": 393 },
|
||||
"end": { "line": 23, "column": 8, "offset": 400 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"depth": 3,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "--feedback--",
|
||||
"position": {
|
||||
"start": { "line": 25, "column": 5, "offset": 406 },
|
||||
"end": { "line": 25, "column": 17, "offset": 418 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 25, "column": 1, "offset": 402 },
|
||||
"end": { "line": 25, "column": 17, "offset": 418 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "Pay attention to the verb in the sentence.",
|
||||
"position": {
|
||||
"start": { "line": 27, "column": 1, "offset": 420 },
|
||||
"end": { "line": 27, "column": 43, "offset": 462 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 27, "column": 1, "offset": 420 },
|
||||
"end": { "line": 27, "column": 43, "offset": 462 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"start": { "line": 1, "column": 1, "offset": 0 },
|
||||
"end": { "line": 28, "column": 1, "offset": 463 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# --description--
|
||||
|
||||
In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
|
||||
|
||||
# --fillInTheBlank--
|
||||
|
||||
## --sentence--
|
||||
|
||||
`A sentence _ paragraph 1`
|
||||
`not enough newlines, so no paragraph 2`
|
||||
|
||||
Sentence in _ 2
|
||||
|
||||
## --blanks--
|
||||
|
||||
`are`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
|
||||
---
|
||||
|
||||
`right`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
@@ -0,0 +1,27 @@
|
||||
# --description--
|
||||
|
||||
In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
|
||||
|
||||
# --fillInTheBlank--
|
||||
|
||||
## --sentence--
|
||||
|
||||
`A sentence _ paragraph 1`
|
||||
|
||||
Sentence in _ 2
|
||||
|
||||
## --blanks--
|
||||
|
||||
`are`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
|
||||
---
|
||||
|
||||
`right`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
@@ -0,0 +1,27 @@
|
||||
# --description--
|
||||
|
||||
In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
|
||||
|
||||
# --fillInTheBlank--
|
||||
|
||||
## --sentence--
|
||||
|
||||
`A sentence _ paragraph 1`
|
||||
|
||||
`Sentence in _ 2`
|
||||
|
||||
## --blanks--
|
||||
|
||||
`are`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
|
||||
---
|
||||
|
||||
`right`
|
||||
|
||||
### --feedback--
|
||||
|
||||
Pay attention to the verb in the sentence.
|
||||
@@ -6,6 +6,32 @@ const mdastToHtml = require('./utils/mdast-to-html');
|
||||
|
||||
const { splitOnThematicBreak } = require('./utils/split-on-thematic-break');
|
||||
|
||||
const NOT_IN_PARAGRAPHS = `Each inline code block in the fillInTheBlank sentence section must in its own paragraph
|
||||
If you have more than one code block, check that they're separated by a blank line
|
||||
Example of bad formatting:
|
||||
\`too close\`
|
||||
\`to each other\`
|
||||
|
||||
Example of good formatting:
|
||||
\`separated\`
|
||||
|
||||
\`by a blank line\`
|
||||
|
||||
`;
|
||||
|
||||
const NOT_IN_CODE_BLOCK = `Each paragraph in the fillInTheBlank sentence section must be inside an inline code block
|
||||
Example of bad formatting:
|
||||
## --sentence--
|
||||
|
||||
This is a sentence
|
||||
|
||||
Example of good formatting:
|
||||
## --sentence--
|
||||
|
||||
\`This is a sentence\`
|
||||
|
||||
`;
|
||||
|
||||
function plugin() {
|
||||
return transformer;
|
||||
function transformer(tree, file) {
|
||||
@@ -24,7 +50,17 @@ function plugin() {
|
||||
}
|
||||
|
||||
function getfillInTheBlank(sentenceNodes, blanksNodes) {
|
||||
const sentence = mdastToHtml(sentenceNodes);
|
||||
const sentenceWithoutCodeBlocks = sentenceNodes.map(node => {
|
||||
node.children.forEach(child => {
|
||||
if (child.type === 'text' && child.value.trim() === '')
|
||||
throw Error(NOT_IN_PARAGRAPHS);
|
||||
if (child.type !== 'inlineCode') throw Error(NOT_IN_CODE_BLOCK);
|
||||
});
|
||||
|
||||
const children = node.children.map(child => ({ ...child, type: 'text' }));
|
||||
return { ...node, children };
|
||||
});
|
||||
const sentence = mdastToHtml(sentenceWithoutCodeBlocks);
|
||||
const blanks = getBlanks(blanksNodes);
|
||||
|
||||
if (!sentence) throw Error('sentence is missing from fill in the blank');
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
const mockFillInTheBlankAST = require('../__fixtures__/ast-fill-in-the-blank.json');
|
||||
const mockFillInTheBlankYouAreAST = require('../__fixtures__/ast-fill-in-the-blank-one-blank.json');
|
||||
const mockFillInTheBlankTwoSentencesAST = require('../__fixtures__/ast-fill-in-the-blank-two-sentences.json');
|
||||
const mockFillInTheBlankBadSentence = require('../__fixtures__/ast-fill-in-the-blank-bad-sentence.json');
|
||||
const mockFillInTheBlankBadParagraph = require('../__fixtures__/ast-fill-in-the-blank-bad-paragraph.json');
|
||||
const addFillInTheBlankQuestion = require('./add-fill-in-the-blank');
|
||||
|
||||
describe('fill-in-the-blanks plugin', () => {
|
||||
@@ -66,6 +69,63 @@ describe('fill-in-the-blanks plugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should extract the sentence from the surrounding inline code block', () => {
|
||||
plugin(mockFillInTheBlankAST, file);
|
||||
const testObject = file.data.fillInTheBlank;
|
||||
|
||||
expect(testObject.sentence).toBe(
|
||||
'<p>Hello, You _ the new graphic designer, _? _ to meet you!</p>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should extract sentences from multiple inline code blocks', () => {
|
||||
plugin(mockFillInTheBlankTwoSentencesAST, file);
|
||||
const testObject = file.data.fillInTheBlank;
|
||||
|
||||
expect(testObject.sentence).toBe(
|
||||
`<p>A sentence _ paragraph 1</p>
|
||||
<p>Sentence in _ 2</p>`
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw if a sentence is not inside an inline code block', () => {
|
||||
expect(() => {
|
||||
plugin(mockFillInTheBlankBadSentence, file);
|
||||
}).toThrow(
|
||||
`Each paragraph in the fillInTheBlank sentence section must be inside an inline code block
|
||||
Example of bad formatting:
|
||||
## --sentence--
|
||||
|
||||
This is a sentence
|
||||
|
||||
Example of good formatting:
|
||||
## --sentence--
|
||||
|
||||
\`This is a sentence\`
|
||||
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw if there are multiple inline code blocks in the same paragraph', () => {
|
||||
expect(() => {
|
||||
plugin(mockFillInTheBlankBadParagraph, file);
|
||||
}).toThrow(
|
||||
`Each inline code block in the fillInTheBlank sentence section must in its own paragraph
|
||||
If you have more than one code block, check that they're separated by a blank line
|
||||
Example of bad formatting:
|
||||
\`too close\`
|
||||
\`to each other\`
|
||||
|
||||
Example of good formatting:
|
||||
\`separated\`
|
||||
|
||||
\`by a blank line\`
|
||||
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle one blank', () => {
|
||||
plugin(mockFillInTheBlankYouAreAST, file);
|
||||
const testObject = file.data.fillInTheBlank;
|
||||
|
||||
Reference in New Issue
Block a user