mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Renames, centralize sanitize(), StemTemplate
This commit is contained in:
parent
7dbbfbfe65
commit
bb5b2b5e61
17 changed files with 88 additions and 72 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { textType } from "src/components/GiftTemplate/templates/TextTypeTemplate";
|
||||
import { FormatTextTemplate } from "src/components/GiftTemplate/templates/TextTypeTemplate";
|
||||
import { TextFormat } from "gift-pegjs";
|
||||
|
||||
describe('TextType', () => {
|
||||
|
|
@ -8,7 +8,7 @@ describe('TextType', () => {
|
|||
format: 'moodle'
|
||||
};
|
||||
const expectedOutput = 'Hello, world! 5 > 3, right?';
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with newlines correctly', () => {
|
||||
|
|
@ -17,7 +17,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
const expectedOutput = 'Hello,<br>world!<br>5 > 3, right?';
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with LaTeX correctly', () => {
|
||||
|
|
@ -31,7 +31,7 @@ describe('TextType', () => {
|
|||
// by running the test and copying the "Received string:" in jest output
|
||||
// when it fails (assuming the output is correct)
|
||||
const expectedOutput = '<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8641em;"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>';
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with two equations (inline and separate) correctly', () => {
|
||||
|
|
@ -41,7 +41,7 @@ describe('TextType', () => {
|
|||
};
|
||||
// hint: katex-display is the class that indicates a separate equation
|
||||
const expectedOutput = '<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>+</mo><mi>b</mi><mo>=</mo><mi>c</mi></mrow><annotation encoding="application/x-tex">a + b = c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">c</span></span></span></span> ? <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8641em;"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>';
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with a katex matrix correctly', () => {
|
||||
|
|
@ -51,7 +51,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
const expectedOutput = 'Donnez le déterminant de la matrice suivante.<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow></mrow><annotation encoding="application/x-tex"></annotation></semantics></math></span><span class="katex-html" aria-hidden="true"></span></span>\\begin{pmatrix}<br> a&b \\\\<br> c&d<br>\\end{pmatrix}';
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with Markdown correctly', () => {
|
||||
|
|
@ -61,7 +61,7 @@ describe('TextType', () => {
|
|||
};
|
||||
// TODO: investigate why the output has an extra newline
|
||||
const expectedOutput = '<strong>Bold</strong>\n';
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with HTML correctly', () => {
|
||||
|
|
@ -70,7 +70,7 @@ describe('TextType', () => {
|
|||
format: 'html'
|
||||
};
|
||||
const expectedOutput = '<em>yes</em>';
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format plain text correctly', () => {
|
||||
|
|
@ -79,7 +79,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
const expectedOutput = 'Just plain text';
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
expect(FormatTextTemplate(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
// Add more tests for other formats if needed
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ exports[`MultipleChoice snapshot test 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Sample Stem</p><span style="
|
||||
" class="present-question-stem">Sample Stem</p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
@ -169,7 +169,7 @@ exports[`MultipleChoice snapshot test with 2 images using markdown text format 1
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
"><img src="https://via.placeholder.com/150" alt = "Sample Image"/></p><span style="
|
||||
" class="present-question-stem"><img src="https://via.placeholder.com/150" alt = "Sample Image"/></p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
@ -337,7 +337,7 @@ exports[`MultipleChoice snapshot test with Moodle text format 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Sample Stem</p><span style="
|
||||
" class="present-question-stem">Sample Stem</p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
@ -466,7 +466,7 @@ exports[`MultipleChoice snapshot test with image 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Sample Stem with Image</p><span style="
|
||||
" class="present-question-stem">Sample Stem with Image</p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
@ -631,7 +631,7 @@ exports[`MultipleChoice snapshot test with image using markdown text format 1`]
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Sample Stem with Image
|
||||
" class="present-question-stem">Sample Stem with Image
|
||||
</p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
|
|
@ -800,7 +800,7 @@ exports[`MultipleChoice snapshot test with katex 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>z</mi><mi>z</mi><mi>z</mi></mrow><mrow><mi>y</mi><mi>y</mi><mi>y</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\\frac{zzz}{yyy}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.988em;vertical-align:-0.8804em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">yyy</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.04398em;">zzz</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><span style="
|
||||
" class="present-question-stem"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>z</mi><mi>z</mi><mi>z</mi></mrow><mrow><mi>y</mi><mi>y</mi><mi>y</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\\frac{zzz}{yyy}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.988em;vertical-align:-0.8804em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">yyy</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.04398em;">zzz</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
@ -929,7 +929,7 @@ exports[`MultipleChoice snapshot test with katex, using html text format 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>z</mi><mi>z</mi><mi>z</mi></mrow><mrow><mi>y</mi><mi>y</mi><mi>y</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\\frac{zzz}{yyy}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.988em;vertical-align:-0.8804em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">yyy</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.04398em;">zzz</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><span style="
|
||||
" class="present-question-stem"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>z</mi><mi>z</mi><mi>z</mi></mrow><mrow><mi>y</mi><mi>y</mi><mi>y</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\\frac{zzz}{yyy}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.988em;vertical-align:-0.8804em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">yyy</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.04398em;">zzz</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><span style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Choisir une réponse:</span>
|
||||
<div class='multiple-choice-answers-container'>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import { textType } from './TextTypeTemplate';
|
||||
import { ParagraphStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { Description } from 'gift-pegjs';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type DescriptionOptions = TemplateOptions & Description;
|
||||
|
||||
|
|
@ -15,9 +13,7 @@ export default function DescriptionTemplate({ title, formattedStem}: Description
|
|||
type: 'Description',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">
|
||||
${textType(formattedStem)}
|
||||
</p>`
|
||||
StemTemplate({formattedStem}),
|
||||
]
|
||||
})}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import GlobalFeedbackTemplate from './GlobalFeedbackTemplate';
|
||||
import { ParagraphStyle, TextAreaStyle } from '../constants';
|
||||
import { TextAreaStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { EssayQuestion } from 'gift-pegjs';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type EssayOptions = TemplateOptions & EssayQuestion;
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ export default function EssayTemplate({ title, formattedStem, formattedGlobalFee
|
|||
type: 'Développement',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
`<textarea class="gift-textarea" style="${TextAreaStyle(
|
||||
state.theme
|
||||
)}" placeholder="Entrez votre réponse ici..."></textarea>`,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import {FormatTextTemplate} from './TextTypeTemplate';
|
||||
import { state } from '.';
|
||||
import { theme } from '../constants';
|
||||
import { TextFormat } from 'gift-pegjs';
|
||||
|
|
@ -20,7 +20,7 @@ export default function GlobalFeedbackTemplate({ format, text }: GlobalFeedbackO
|
|||
|
||||
return (format && text)
|
||||
? `<div style="${Container}">
|
||||
<p>${textType({format: format, text: text})}</p>
|
||||
<p>${FormatTextTemplate({format: format, text: text})}</p>
|
||||
</div>`
|
||||
: ``;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import {FormatTextTemplate} from './TextTypeTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import { ParagraphStyle, SelectStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { MatchingQuestion } from 'gift-pegjs';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type MatchingOptions = TemplateOptions & MatchingQuestion;
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ export default function MatchingTemplate({
|
|||
type: 'Appariement',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
MatchAnswers({ choices: matchPairs }),
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||
]
|
||||
|
|
@ -66,7 +67,7 @@ function MatchAnswers({ choices }: MatchAnswerOptions): string {
|
|||
.map(({ formattedSubquestion }) => {
|
||||
return `
|
||||
<div style="${OptionTable} ${ParagraphStyle(state.theme)}">
|
||||
${textType(formattedSubquestion)}
|
||||
${FormatTextTemplate(formattedSubquestion)}
|
||||
</div>
|
||||
<div>
|
||||
<select class="gift-select" style="${SelectStyle(state.theme)} ${Dropdown}">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { nanoid } from 'nanoid';
|
||||
import { TemplateOptions } from './types';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import {FormatTextTemplate} from './TextTypeTemplate';
|
||||
import AnswerIcon from './AnswerIconTemplate';
|
||||
import { state } from '.';
|
||||
import { ParagraphStyle, theme } from '../constants';
|
||||
|
|
@ -42,7 +42,7 @@ export default function MultipleChoiceAnswersTemplate({ choices }: MultipleChoic
|
|||
}" id="${inputId}" name="${id}">
|
||||
${AnswerWeight({ correct: isCorrectOption, weight: weight })}
|
||||
<label style="${CustomLabel} ${ParagraphStyle(state.theme)}" for="${inputId}">
|
||||
${textType(formattedText)}
|
||||
${FormatTextTemplate(formattedText)}
|
||||
</label>
|
||||
${AnswerIcon({ correct: isCorrectOption })}
|
||||
${AnswerFeedback({ formattedFeedback: formattedFeedback })}
|
||||
|
|
@ -86,5 +86,5 @@ function AnswerFeedback({ formattedFeedback }: AnswerFeedbackOptions): string {
|
|||
color: ${theme(state.theme, 'teal700', 'gray700')};
|
||||
`;
|
||||
|
||||
return formattedFeedback ? `<span style="${Container}">${textType(formattedFeedback)}</span>` : ``;
|
||||
return formattedFeedback ? `<span style="${Container}">${FormatTextTemplate(formattedFeedback)}</span>` : ``;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@ import { TemplateOptions } from './types';
|
|||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import MultipleChoiceAnswers from './MultipleChoiceAnswersTemplate';
|
||||
import { ParagraphStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type MultipleChoiceOptions = TemplateOptions & MultipleChoiceQuestion;
|
||||
|
||||
|
|
@ -22,7 +20,7 @@ export default function MultipleChoiceTemplate({
|
|||
type: 'Choix multiple',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}" class="present-question-stem">${textType(formattedStem)}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
MultipleChoiceAnswers({ choices: choices }),
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import { textType } from './TextTypeTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import { ParagraphStyle, InputStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { NumericalAnswer, NumericalQuestion } from 'gift-pegjs';
|
||||
import { isHighLowNumericalAnswer, isRangeNumericalAnswer, isSimpleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type NumericalOptions = TemplateOptions & NumericalQuestion;
|
||||
type NumericalAnswerOptions = TemplateOptions & Pick<NumericalQuestion, 'choices'>;
|
||||
|
|
@ -23,7 +23,7 @@ export default function NumericalTemplate({
|
|||
type: 'Numérique',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
NumericalAnswers({ choices: choices }),
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import {FormatTextTemplate} from './TextTypeTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import { ParagraphStyle, InputStyle } from '../constants';
|
||||
import { state } from './index';
|
||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type ShortAnswerOptions = TemplateOptions & ShortAnswerQuestion;
|
||||
type AnswerOptions = TemplateOptions & Pick<ShortAnswerQuestion, 'choices'>;
|
||||
|
|
@ -22,7 +23,7 @@ export default function ShortAnswerTemplate({
|
|||
type: 'Réponse courte',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
Answers({ choices: choices }),
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||
]
|
||||
|
|
@ -31,7 +32,7 @@ export default function ShortAnswerTemplate({
|
|||
|
||||
function Answers({ choices }: AnswerOptions): string {
|
||||
const placeholder = choices
|
||||
.map(({ text }) => textType({ format: '', text: text }))
|
||||
.map(({ text }) => FormatTextTemplate({ format: '', text: text }))
|
||||
.join(', ');
|
||||
return `
|
||||
<div>
|
||||
|
|
|
|||
26
client/src/components/GiftTemplate/templates/StemTemplate.ts
Normal file
26
client/src/components/GiftTemplate/templates/StemTemplate.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import { state } from '.';
|
||||
import { ParagraphStyle } from '../constants';
|
||||
import { BaseQuestion } from 'gift-pegjs';
|
||||
import { FormatTextTemplate } from './TextTypeTemplate';
|
||||
|
||||
// Type is string to allow for custom question type text (e,g, "Multiple Choice")
|
||||
interface StemOptions extends TemplateOptions {
|
||||
formattedStem: BaseQuestion['formattedStem'];
|
||||
}
|
||||
|
||||
export default function StemTemplate({ formattedStem }: StemOptions): string {
|
||||
const Container = `
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
return `
|
||||
<div style="${Container}">
|
||||
<span>
|
||||
<p style="${ParagraphStyle(state.theme)}" class="present-question-stem">
|
||||
${FormatTextTemplate(formattedStem)}
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { marked } from 'marked';
|
||||
import katex from 'katex';
|
||||
import { TextFormat } from 'gift-pegjs';
|
||||
|
||||
import DOMPurify from 'dompurify'; // cleans HTML to prevent XSS attacks, etc.
|
||||
|
||||
export function formatLatex(text: string): string {
|
||||
return text
|
||||
|
|
@ -25,7 +25,7 @@ export function formatLatex(text: string): string {
|
|||
* @see marked
|
||||
* @see katex
|
||||
*/
|
||||
export function textType(formattedText: TextFormat): string {
|
||||
export function FormatTextTemplate(formattedText: TextFormat): string {
|
||||
const formatText = formatLatex(formattedText.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
|
||||
let parsedText = '';
|
||||
switch (formattedText.format) {
|
||||
|
|
@ -33,13 +33,13 @@ export function textType(formattedText: TextFormat): string {
|
|||
case 'moodle':
|
||||
case 'plain':
|
||||
// Replace newlines with <br> tags
|
||||
return formatText.replace(/(?:\r\n|\r|\n)/g, '<br>');
|
||||
return DOMPurify.sanitize(formatText.replace(/(?:\r\n|\r|\n)/g, '<br>'));
|
||||
case 'html':
|
||||
// Strip outer paragraph tags (not a great approach with regex)
|
||||
return formatText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2');
|
||||
return DOMPurify.sanitize(formatText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2'));
|
||||
case 'markdown':
|
||||
parsedText = marked.parse(formatText, { breaks: true }) as string; // https://github.com/markedjs/marked/discussions/3219
|
||||
return parsedText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2');
|
||||
return DOMPurify.sanitize(parsedText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2'));
|
||||
default:
|
||||
throw new Error(`Unsupported text format: ${formattedText.format}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import MultipleChoiceAnswersTemplate from './MultipleChoiceAnswersTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import { TextChoice, TrueFalseQuestion } from 'gift-pegjs';
|
||||
import { ParagraphStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import DOMPurify from 'dompurify';
|
||||
import StemTemplate from './StemTemplate';
|
||||
|
||||
type TrueFalseOptions = TemplateOptions & TrueFalseQuestion;
|
||||
|
||||
|
|
@ -42,7 +39,7 @@ export default function TrueFalseTemplate({
|
|||
type: 'Vrai/Faux',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}" class="present-question-stem">${DOMPurify.sanitize(textType(formattedStem))}</p>`,
|
||||
StemTemplate({formattedStem}),
|
||||
MultipleChoiceAnswersTemplate({ choices: choices }),
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ``
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { FormatTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
interface Props {
|
||||
question: MultipleChoiceQuestion;
|
||||
|
|
@ -31,7 +30,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-container">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
<div className="choices-wrapper mb-1">
|
||||
{question.choices.map((choice, i) => {
|
||||
|
|
@ -48,13 +47,13 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
(choice.isCorrect ? '✅' : '❌')}
|
||||
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||
<div className={`answer-text ${selected}`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedText)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(choice.formattedText) }} />
|
||||
</div>
|
||||
</Button>
|
||||
{choice.formattedFeedback && showAnswer && (
|
||||
<div className="feedback-container mb-1 mt-1/2">
|
||||
{choice.isCorrect ? '✅' : '❌'}
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(choice.formattedFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -63,7 +62,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
{question.formattedGlobalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
import React, { useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { FormatTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
||||
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
interface Props {
|
||||
question: NumericalQuestion;
|
||||
|
|
@ -41,13 +40,13 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-wrapper">
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
<div className="correct-answer-text mb-2">{correctAnswer}</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
|
|
@ -65,7 +64,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
{question.formattedGlobalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{handleOnSubmitAnswer && (
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import React, { useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { FormatTextTemplate } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
interface Props {
|
||||
question: ShortAnswerQuestion;
|
||||
|
|
@ -18,7 +17,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-wrapper">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
|
|
@ -30,7 +29,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
))}
|
||||
</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import '../questionStyle.css';
|
|||
import { Button } from '@mui/material';
|
||||
import { TrueFalseQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { textType } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||
import { FormatTextTemplate } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||
|
||||
interface Props {
|
||||
question: TrueFalseQuestion;
|
||||
|
|
@ -27,7 +27,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-container">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedStem) }} />
|
||||
</div>
|
||||
<div className="choices-wrapper mb-1">
|
||||
<Button
|
||||
|
|
@ -52,18 +52,18 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
|||
{/* selected TRUE, show True feedback if it exists */}
|
||||
{showAnswer && answer && question.trueFormattedFeedback && (
|
||||
<div className="true-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.trueFormattedFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.trueFormattedFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{/* selected FALSE, show False feedback if it exists */}
|
||||
{showAnswer && !answer && question.falseFormattedFeedback && (
|
||||
<div className="false-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.falseFormattedFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.falseFormattedFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{question.formattedGlobalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: FormatTextTemplate(question.formattedGlobalFeedback) }} />
|
||||
</div>
|
||||
)}
|
||||
{!showAnswer && handleOnSubmitAnswer && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue