mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Renames, passing many tests
This commit is contained in:
parent
00e45f0f09
commit
cb46a18370
42 changed files with 516 additions and 817 deletions
5
client/src/Types/QuestionType.tsx
Normal file
5
client/src/Types/QuestionType.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { BaseQuestion } from "gift-pegjs";
|
||||||
|
|
||||||
|
export interface QuestionType {
|
||||||
|
question: BaseQuestion;
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
// TextType.test.ts
|
import { textType } from "src/components/GiftTemplate/templates/TextTypeTemplate";
|
||||||
|
|
||||||
import { textType } from "src/components/GiftTemplate/templates/TextType";
|
|
||||||
import { TextFormat } from "gift-pegjs";
|
import { TextFormat } from "gift-pegjs";
|
||||||
|
|
||||||
describe('TextType', () => {
|
describe('TextType', () => {
|
||||||
it('should format text with basic characters correctly', () => {
|
it('should format text with basic characters correctly', () => {
|
||||||
const input: TextFormat = {
|
const input: TextFormat = {
|
||||||
text: 'Hello, world! 5 > 3, right?',
|
text: 'Hello, world! 5 > 3, right?',
|
||||||
format: 'plain'
|
format: 'moodle'
|
||||||
};
|
};
|
||||||
const expectedOutput = 'Hello, world! 5 > 3, right?';
|
const expectedOutput = 'Hello, world! 5 > 3, right?';
|
||||||
expect(textType({ text: input })).toBe(expectedOutput);
|
expect(textType(input)).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with newlines correctly', () => {
|
it('should format text with newlines correctly', () => {
|
||||||
|
|
@ -19,7 +17,7 @@ describe('TextType', () => {
|
||||||
format: 'plain'
|
format: 'plain'
|
||||||
};
|
};
|
||||||
const expectedOutput = 'Hello,<br>world!<br>5 > 3, right?';
|
const expectedOutput = 'Hello,<br>world!<br>5 > 3, right?';
|
||||||
expect(textType({ text: input })).toBe(expectedOutput);
|
expect(textType(input)).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with LaTeX correctly', () => {
|
it('should format text with LaTeX correctly', () => {
|
||||||
|
|
@ -33,17 +31,17 @@ describe('TextType', () => {
|
||||||
// by running the test and copying the "Received string:" in jest output
|
// by running the test and copying the "Received string:" in jest output
|
||||||
// when it fails (assuming the output is correct)
|
// 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>';
|
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({ text: input })).toContain(expectedOutput);
|
expect(textType(input)).toContain(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with two equations (inline and separate) correctly', () => {
|
it('should format text with two equations (inline and separate) correctly', () => {
|
||||||
const input: TextFormat = {
|
const input: TextFormat = {
|
||||||
text: '$a + b = c$ ? $$E=mc^2$$',
|
text: '$a + b = c$ ? $$E=mc^2$$',
|
||||||
format: 'plain'
|
format: 'moodle'
|
||||||
};
|
};
|
||||||
// hint: katex-display is the class that indicates a separate equation
|
// 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>';
|
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({ text: input })).toContain(expectedOutput);
|
expect(textType(input)).toContain(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with a katex matrix correctly', () => {
|
it('should format text with a katex matrix correctly', () => {
|
||||||
|
|
@ -53,7 +51,7 @@ describe('TextType', () => {
|
||||||
format: 'plain'
|
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}';
|
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({ text: input })).toContain(expectedOutput);
|
expect(textType(input)).toContain(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with Markdown correctly', () => {
|
it('should format text with Markdown correctly', () => {
|
||||||
|
|
@ -63,7 +61,7 @@ describe('TextType', () => {
|
||||||
};
|
};
|
||||||
// TODO: investigate why the output has an extra newline
|
// TODO: investigate why the output has an extra newline
|
||||||
const expectedOutput = '<strong>Bold</strong>\n';
|
const expectedOutput = '<strong>Bold</strong>\n';
|
||||||
expect(textType({ text: input })).toBe(expectedOutput);
|
expect(textType(input)).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format text with HTML correctly', () => {
|
it('should format text with HTML correctly', () => {
|
||||||
|
|
@ -72,7 +70,7 @@ describe('TextType', () => {
|
||||||
format: 'html'
|
format: 'html'
|
||||||
};
|
};
|
||||||
const expectedOutput = '<em>yes</em>';
|
const expectedOutput = '<em>yes</em>';
|
||||||
expect(textType({ text: input })).toBe(expectedOutput);
|
expect(textType(input)).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format plain text correctly', () => {
|
it('should format plain text correctly', () => {
|
||||||
|
|
@ -81,7 +79,7 @@ describe('TextType', () => {
|
||||||
format: 'plain'
|
format: 'plain'
|
||||||
};
|
};
|
||||||
const expectedOutput = 'Just plain text';
|
const expectedOutput = 'Just plain text';
|
||||||
expect(textType({ text: input })).toBe(expectedOutput);
|
expect(textType(input)).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add more tests for other formats if needed
|
// Add more tests for other formats if needed
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import AnswerIcon from 'src/components/GiftTemplate/templates/AnswerIcon';
|
import AnswerIcon from 'src/components/GiftTemplate/templates/AnswerIconTemplate';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
describe('AnswerIcon', () => {
|
describe('AnswerIcon', () => {
|
||||||
|
|
|
||||||
|
|
@ -2,99 +2,100 @@ import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { MultipleChoice } from 'src/components/GiftTemplate/templates';
|
import { MultipleChoice } from 'src/components/GiftTemplate/templates';
|
||||||
import { TemplateOptions, MultipleChoice as MultipleChoiceType } from 'src/components/GiftTemplate/templates/types';
|
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||||
|
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
// Mock the nanoid function
|
// Mock the nanoid function
|
||||||
jest.mock('nanoid', () => ({
|
jest.mock('nanoid', () => ({
|
||||||
nanoid: jest.fn(() => 'mocked-id')
|
nanoid: jest.fn(() => 'mocked-id')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockProps: TemplateOptions & MultipleChoiceType = {
|
const mockProps: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title',
|
title: 'Sample Title',
|
||||||
stem: { format: 'plain' , text: 'Sample Stem'},
|
formattedStem: { format: 'plain' , text: 'Sample Stem'},
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
{ formattedText: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
||||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
{ formattedText: { format: 'plain', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
formattedGlobalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const katekMock: TemplateOptions & MultipleChoiceType = {
|
const katekMock: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title',
|
title: 'Sample Title',
|
||||||
stem: { format: 'plain' , text: '$$\\frac{zzz}{yyy}$$'},
|
formattedStem: { format: 'plain' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
{ formattedText: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
||||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 }
|
{ formattedText: { format: 'plain', text: 'Choice 2' }, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
formattedGlobalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const imageMock: TemplateOptions & MultipleChoiceType = {
|
const imageMock: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title with Image',
|
title: 'Sample Title with Image',
|
||||||
stem: { format: 'plain', text: 'Sample Stem with Image' },
|
formattedStem: { format: 'plain', text: 'Sample Stem with Image' },
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'plain', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
{ formattedText: { format: 'plain', text: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
{ formattedText: { format: 'plain', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
||||||
{ text: { format: 'plain', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, feedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
{ formattedText: { format: 'plain', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback with Image' }
|
formattedGlobalFeedback: { format: 'plain', text: 'Sample Global Feedback with Image' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockMoodle: TemplateOptions & MultipleChoiceType = {
|
const mockMoodle: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title',
|
title: 'Sample Title',
|
||||||
stem: { format: 'moodle' , text: 'Sample Stem'},
|
formattedStem: { format: 'moodle' , text: 'Sample Stem'},
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'moodle' , text: 'Choice 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
{ formattedText: { format: 'moodle' , text: 'Choice 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
||||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
{ formattedText: { format: 'plain', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
formattedGlobalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const mockHTML: TemplateOptions & MultipleChoiceType = {
|
const mockHTML: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title',
|
title: 'Sample Title',
|
||||||
stem: { format: 'html' , text: '$$\\frac{zzz}{yyy}$$'},
|
formattedStem: { format: 'html' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'html' , text: 'Choice 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
{ formattedText: { format: 'html' , text: 'Choice 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
||||||
{ text: { format: 'html', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
{ formattedText: { format: 'html', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'html', text: 'Sample Global Feedback' }
|
formattedGlobalFeedback: { format: 'html', text: 'Sample Global Feedback' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockMarkdown: TemplateOptions & MultipleChoiceType = {
|
const mockMarkdown: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title with Image',
|
title: 'Sample Title with Image',
|
||||||
stem: { format: 'markdown', text: 'Sample Stem with Image' },
|
formattedStem: { format: 'markdown', text: 'Sample Stem with Image' },
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
{ formattedText: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||||
{ text: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
{ formattedText: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
||||||
{ text: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, feedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
{ formattedText: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'markdown', text: 'Sample Global Feedback with Image' }
|
formattedGlobalFeedback: { format: 'markdown', text: 'Sample Global Feedback with Image' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockMarkdownTwoImages: TemplateOptions & MultipleChoiceType = {
|
const mockMarkdownTwoImages: TemplateOptions & MultipleChoiceQuestion = {
|
||||||
type: 'MC',
|
type: 'MC',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
title: 'Sample Title with Image',
|
title: 'Sample Title with Image',
|
||||||
stem: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt = "Sample Image"/>' },
|
formattedStem: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt = "Sample Image"/>' },
|
||||||
choices: [
|
choices: [
|
||||||
{ text: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
{ formattedText: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||||
{ text: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
{ formattedText: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, weight: 1 },
|
||||||
{ text: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, feedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
{ formattedText: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Image Feedback' }, weight: 1 }
|
||||||
],
|
],
|
||||||
globalFeedback: { format: 'markdown', text: 'Sample Global Feedback with Image' }
|
formattedGlobalFeedback: { format: 'markdown', text: 'Sample Global Feedback with Image' }
|
||||||
};
|
};
|
||||||
|
|
||||||
test('MultipleChoice snapshot test', () => {
|
test('MultipleChoice snapshot test', () => {
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,40 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import Numerical from 'src/components/GiftTemplate/templates/Numerical';
|
import Numerical from 'src/components/GiftTemplate/templates/NumericalTemplate';
|
||||||
import { TemplateOptions, Numerical as NumericalType } from 'src/components/GiftTemplate/templates/types';
|
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||||
|
import { parse, NumericalQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
// Mock the nanoid function
|
// Mock the nanoid function
|
||||||
jest.mock('nanoid', () => ({
|
jest.mock('nanoid', () => ({
|
||||||
nanoid: jest.fn(() => 'mocked-id')
|
nanoid: jest.fn(() => 'mocked-id')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const plainTextMock: TemplateOptions & NumericalType = {
|
const plainTextMock: TemplateOptions & NumericalQuestion =
|
||||||
type: 'Numerical',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Numerical Title:: Sample Stem {#=42#Correct!=43#Incorrect!####Sample Global Feedback}
|
||||||
title: 'Sample Numerical Title',
|
`)[0] as NumericalQuestion;
|
||||||
stem: { format: 'plain', text: 'Sample Stem' },
|
|
||||||
choices: [
|
|
||||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'plain', text: 'Correct!' } },
|
|
||||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'plain', text: 'Incorrect!' } }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const htmlMock: TemplateOptions & NumericalType = {
|
const htmlMock: TemplateOptions & NumericalQuestion =
|
||||||
type: 'Numerical',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Numerical Title::
|
||||||
title: 'Sample Numerical Title',
|
[html]$$\\frac\\{zzz\\}\\{yyy\\}$$ {#
|
||||||
stem: { format: 'html', text: '$$\\frac{zzz}{yyy}$$' },
|
=42#Correct
|
||||||
choices: [
|
=43#Incorrect!
|
||||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'html', text: 'Correct!' } },
|
####Sample Global Feedback
|
||||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'html', text: 'Incorrect!' } }
|
}
|
||||||
],
|
`)[0] as NumericalQuestion;
|
||||||
globalFeedback: { format: 'html', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const moodleMock: TemplateOptions & NumericalType = {
|
const moodleMock: TemplateOptions & NumericalQuestion =
|
||||||
type: 'Numerical',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Numerical Title::[moodle]Sample Stem {#=42#Correct!=43#Incorrect!####Sample Global Feedback}
|
||||||
title: 'Sample Numerical Title',
|
`)[0] as NumericalQuestion;
|
||||||
stem: { format: 'moodle', text: 'Sample Stem' },
|
|
||||||
choices: [
|
const imageMock: TemplateOptions & NumericalQuestion =
|
||||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'moodle', text: 'Correct!' } },
|
parse(`
|
||||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'moodle', text: 'Incorrect!' } }
|
::Sample Numerical Title with Image::[markdown]Sample Stem with Image {#=42#Correct!=43#Incorrect! ####Sample Global Feedback with Image}
|
||||||
],
|
`)[0] as NumericalQuestion;
|
||||||
globalFeedback: { format: 'moodle', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const imageMock: TemplateOptions & NumericalType = {
|
|
||||||
type: 'Numerical',
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
title: 'Sample Numerical Title with Image',
|
|
||||||
stem: { format: 'plain', text: 'Sample Stem with Image' },
|
|
||||||
choices: [
|
|
||||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'plain', text: 'Correct!' } },
|
|
||||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'plain', text: 'Incorrect!' } },
|
|
||||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 44}, feedback: { format: 'plain', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' } }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback with Image' }
|
|
||||||
};
|
|
||||||
|
|
||||||
test('Numerical snapshot test with plain text', () => {
|
test('Numerical snapshot test with plain text', () => {
|
||||||
const { asFragment } = render(<Numerical {...plainTextMock} />);
|
const { asFragment } = render(<Numerical {...plainTextMock} />);
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,35 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import ShortAnswer from 'src/components/GiftTemplate/templates/ShortAnswer';
|
import ShortAnswer from 'src/components/GiftTemplate/templates/ShortAnswerTemplate';
|
||||||
import { TemplateOptions, ShortAnswer as ShortAnswerType } from 'src/components/GiftTemplate/templates/types';
|
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||||
|
import { parse, ShortAnswerQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
// Mock the nanoid function
|
// Mock the nanoid function
|
||||||
jest.mock('nanoid', () => ({
|
jest.mock('nanoid', () => ({
|
||||||
nanoid: jest.fn(() => 'mocked-id')
|
nanoid: jest.fn(() => 'mocked-id')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const plainTextMock: TemplateOptions & ShortAnswerType = {
|
const plainTextMock: TemplateOptions & ShortAnswerQuestion =
|
||||||
type: 'Short',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Short Answer Title:: Sample Stem {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####Sample Global Feedback}
|
||||||
title: 'Sample Short Answer Title',
|
`)[0] as ShortAnswerQuestion;
|
||||||
stem: { format: 'plain', text: 'Sample Stem' },
|
|
||||||
choices: [
|
|
||||||
{ text: { format: 'plain' , text: 'Answer 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
|
||||||
{ text: { format: 'plain' , text: 'Answer 2'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const katexMock: TemplateOptions & ShortAnswerType = {
|
const katexMock: TemplateOptions & ShortAnswerQuestion =
|
||||||
type: 'Short',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Short Answer Title:: $$\\frac\\{zzz\\}\\{yyy\\}$$ {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####[html]Sample Global Feedback}
|
||||||
title: 'Sample Short Answer Title',
|
`)[0] as ShortAnswerQuestion;
|
||||||
stem: { format: 'html', text: '$$\\frac{zzz}{yyy}$$' },
|
|
||||||
choices: [
|
|
||||||
{ text: { format: 'html' , text: 'Answer 1'}, isCorrect: true, feedback: { format: 'html' , text: 'Correct!'}, weight: 1 },
|
|
||||||
{ text: { format: 'html' , text: 'Answer 2'}, isCorrect: true, feedback: { format: 'moodle' , text: 'Correct!'}, weight: 1 }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'html', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const moodleMock: TemplateOptions & ShortAnswerType = {
|
|
||||||
type: 'Short',
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
title: 'Sample Short Answer Title',
|
|
||||||
stem: { format: 'moodle', text: 'Sample Stem' },
|
|
||||||
choices: [
|
|
||||||
{ text: { format: 'moodle' , text: 'Answer 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
|
||||||
{ text: { format: 'moodle' , text: 'Answer 2'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'moodle', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const imageMock: TemplateOptions & ShortAnswerType = {
|
const moodleMock: TemplateOptions & ShortAnswerQuestion =
|
||||||
type: 'Short',
|
parse(`
|
||||||
hasEmbeddedAnswers: false,
|
::Sample Short Answer Title:: Sample Stem {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####[moodle]Sample Global Feedback}
|
||||||
title: 'Sample Short Answer Title with Image',
|
`)[0] as ShortAnswerQuestion;
|
||||||
stem: { format: 'markdown', text: 'Sample Stem with Image' },
|
|
||||||
choices: [
|
|
||||||
{ text: { format: 'markdown', text: 'Answer 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
|
||||||
{ text: { format: 'markdown', text: 'Answer 2' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
|
||||||
{ text: { format: 'markdown', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 }
|
|
||||||
],
|
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback with Image' }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const imageMock: TemplateOptions & ShortAnswerQuestion =
|
||||||
|
parse(`
|
||||||
|
::Sample Short Answer Title with Image::[markdown]Sample Stem with Image  {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####Sample Global Feedback with Image}
|
||||||
|
`)[0] as ShortAnswerQuestion;
|
||||||
|
|
||||||
test('ShortAnswer snapshot test with plain text', () => {
|
test('ShortAnswer snapshot test with plain text', () => {
|
||||||
const { asFragment } = render(<ShortAnswer {...plainTextMock} />);
|
const { asFragment } = render(<ShortAnswer {...plainTextMock} />);
|
||||||
|
|
|
||||||
|
|
@ -2,56 +2,27 @@ import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import TrueFalse from 'src/components/GiftTemplate/templates';
|
import TrueFalse from 'src/components/GiftTemplate/templates';
|
||||||
import { TemplateOptions, TrueFalse as TrueFalseType } from 'src/components/GiftTemplate/templates/types';
|
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||||
|
import { parse, ShortAnswerQuestion, TrueFalseQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
// Mock the nanoid function
|
// Mock the nanoid function
|
||||||
jest.mock('nanoid', () => ({
|
jest.mock('nanoid', () => ({
|
||||||
nanoid: jest.fn(() => 'mocked-id')
|
nanoid: jest.fn(() => 'mocked-id')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const plainTextMock: TemplateOptions & TrueFalseType = {
|
const plainTextMock: TemplateOptions & TrueFalseQuestion =
|
||||||
type: 'TF',
|
parse(`::Sample True/False Title::Sample Stem {T#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
title: 'Sample True/False Title',
|
|
||||||
stem: { format: 'plain', text: 'Sample Stem' },
|
|
||||||
isTrue: true,
|
|
||||||
trueFeedback: { format: 'plain', text: 'Correct!' },
|
|
||||||
falseFeedback: { format: 'plain', text: 'Incorrect!' },
|
|
||||||
globalFeedback: { format: 'plain', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const katexMock: TemplateOptions & TrueFalseType = {
|
const katexMock: TemplateOptions & TrueFalseQuestion =
|
||||||
type: 'TF',
|
parse(`::Sample True/False Title::$$\\frac{zzz}{yyy}$$ {T#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
title: 'Sample True/False Title',
|
|
||||||
stem: { format: 'html', text: '$$\\frac{zzz}{yyy}$$' },
|
|
||||||
isTrue: true,
|
|
||||||
trueFeedback: { format: 'moodle', text: 'Correct!' },
|
|
||||||
falseFeedback: { format: 'html', text: 'Incorrect!' },
|
|
||||||
globalFeedback: { format: 'markdown', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const moodleMock: TemplateOptions & TrueFalseType = {
|
const moodleMock: TemplateOptions & TrueFalseQuestion =
|
||||||
type: 'TF',
|
parse(`::Sample True/False Title::[moodle]Sample Stem{TRUE#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
title: 'Sample True/False Title',
|
|
||||||
stem: { format: 'moodle', text: 'Sample Stem' },
|
|
||||||
isTrue: true,
|
|
||||||
trueFeedback: { format: 'moodle', text: 'Correct!' },
|
|
||||||
falseFeedback: { format: 'moodle', text: 'Incorrect!' },
|
|
||||||
globalFeedback: { format: 'moodle', text: 'Sample Global Feedback' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const imageMock: TemplateOptions & TrueFalseType = {
|
const imageMock: TemplateOptions & ShortAnswerQuestion =
|
||||||
type: 'TF',
|
parse(`::Sample Short Answer Title with Image::
|
||||||
hasEmbeddedAnswers: false,
|
[markdown]Sample Stem with Image 
|
||||||
title: 'Sample Short Answer Title with Image',
|
{=A =B =C####[html]<img src="https\\://via.placeholder.com/150" alt="Sample Image" />}`)[0] as ShortAnswerQuestion;
|
||||||
stem: { format: 'plain', text: 'Sample Stem with Image' },
|
|
||||||
trueFeedback: { format: 'moodle', text: 'Correct!' },
|
|
||||||
isTrue: true,
|
|
||||||
falseFeedback: { format: 'moodle', text: 'Incorrect!' },
|
|
||||||
globalFeedback: { format: 'plain', text: '<img src="https://via.placeholder.com/150" alt="Sample Image" />' }
|
|
||||||
};
|
|
||||||
|
|
||||||
test('TrueFalse snapshot test with plain text', () => {
|
test('TrueFalse snapshot test with plain text', () => {
|
||||||
const { asFragment } = render(<TrueFalse {...plainTextMock} />);
|
const { asFragment } = render(<TrueFalse {...plainTextMock} />);
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
||||||
</div>
|
</div>
|
||||||
<p style="
|
<p style="
|
||||||
color: hsl(0, 0%, 0%);
|
color: hsl(0, 0%, 0%);
|
||||||
">Sample Stem with Image
|
">Sample Stem with Image <img src="https://example.com/cat.jpg" alt="">
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<span style="
|
<span style="
|
||||||
|
|
@ -59,9 +59,7 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
" placeholder="Answer 1
|
" placeholder="Answer 1, Answer 2">
|
||||||
, Answer 2
|
|
||||||
, <img src="https://via.placeholder.com/150" alt="Sample Image" />">
|
|
||||||
</div>
|
</div>
|
||||||
<div style="
|
<div style="
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -73,7 +71,8 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
||||||
">
|
">
|
||||||
<p>Sample Global Feedback with Image</p>
|
<p>Sample Global Feedback with Image
|
||||||
|
</p>
|
||||||
</div></section>
|
</div></section>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,17 @@ import React from 'react';
|
||||||
import { render, fireEvent, screen, act } from '@testing-library/react';
|
import { render, fireEvent, screen, act } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import TrueFalseQuestion from 'src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay';
|
import TrueFalseQuestionDisplay from 'src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay';
|
||||||
|
import { parse, TrueFalseQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
describe('TrueFalseQuestion Component', () => {
|
describe('TrueFalseQuestion Component', () => {
|
||||||
const mockHandleSubmitAnswer = jest.fn();
|
const mockHandleSubmitAnswer = jest.fn();
|
||||||
const sampleStem = 'Sample question stem';
|
const sampleStem = 'Sample True False Question';
|
||||||
|
const trueFalseQuestion =
|
||||||
|
parse(`${sampleStem}{T}`)[0] as TrueFalseQuestion;
|
||||||
|
|
||||||
const sampleProps = {
|
const sampleProps = {
|
||||||
questionTitle: 'Sample True/False Question',
|
question: trueFalseQuestion,
|
||||||
correctAnswer: true,
|
|
||||||
handleOnSubmitAnswer: mockHandleSubmitAnswer,
|
handleOnSubmitAnswer: mockHandleSubmitAnswer,
|
||||||
showAnswer: false
|
showAnswer: false
|
||||||
};
|
};
|
||||||
|
|
@ -19,7 +21,7 @@ describe('TrueFalseQuestion Component', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<TrueFalseQuestion questionContent={{ text: sampleStem, format: 'plain' }} {...sampleProps} />
|
<TrueFalseQuestionDisplay {...sampleProps} />
|
||||||
</MemoryRouter>);
|
</MemoryRouter>);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,20 @@ import React from 'react';
|
||||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { Question } from 'gift-pegjs';
|
|
||||||
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
|
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
|
||||||
import { parse } from 'gift-pegjs';
|
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||||
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
|
||||||
const mockGiftQuestions = parse(
|
const mockGiftQuestions = parse(
|
||||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||||
|
|
||||||
::Sample Question 2:: Sample Question 2 {T}`);
|
::Sample Question 2:: Sample Question 2 {T}`);
|
||||||
|
|
||||||
const mockQuestions: Question[] = mockGiftQuestions.map((question, index) => {
|
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||||
if (question.type !== "Category")
|
if (question.type !== "Category")
|
||||||
question.id = (index + 1).toString();
|
question.id = (index + 1).toString();
|
||||||
const newMockQuestion = question;
|
const newMockQuestion = question;
|
||||||
return newMockQuestion;
|
return {question : newMockQuestion as BaseQuestion};
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockSubmitAnswer = jest.fn();
|
const mockSubmitAnswer = jest.fn();
|
||||||
|
|
|
||||||
|
|
@ -1,256 +1,21 @@
|
||||||
import Template, { ErrorTemplate } from './templates';
|
import Template, { ErrorTemplate } from './templates';
|
||||||
import { GIFTQuestion } from './templates/types';
|
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
import { parse } from 'gift-pegjs';
|
||||||
|
|
||||||
|
const multiple = parse(`
|
||||||
|
Who's buried in Grant's tomb? {~%-50%Grant=%50%Jefferson=%50%no one####Not sure? There are many answers for this question so do not fret. Not sure? There are many answers for this question so do not fret.}
|
||||||
|
|
||||||
|
Grant is _____ in Grant's tomb. {=buried#No one is buried there.=entombed~living}
|
||||||
|
|
||||||
|
Grant is buried in Grant's tomb. {FALSE}
|
||||||
|
|
||||||
|
Who's buried in Grant's tomb? {=no one=nobody}
|
||||||
|
|
||||||
|
When was Ulysses S. Grant born? {#1822:5}
|
||||||
|
|
||||||
|
What is the capital of Canada? {=Canada -> Ottawa =Italy -> Rome =Japan -> Tokyo}
|
||||||
|
`);
|
||||||
|
|
||||||
const multiple: GIFTQuestion[] = [
|
|
||||||
{
|
|
||||||
type: 'MC',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'markdown', text: "Who's buried in Grant's \r\n tomb?" },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'Not sure? There are many answers for this question so do not fret. Not sure? There are many answers for this question so do not fret.'
|
|
||||||
},
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: -50,
|
|
||||||
text: { format: 'moodle', text: 'Grant' },
|
|
||||||
feedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: 50,
|
|
||||||
text: { format: 'moodle', text: 'Jefferson' },
|
|
||||||
feedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: 50,
|
|
||||||
text: { format: 'moodle', text: 'no one' },
|
|
||||||
feedback: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'MC',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'moodle', text: "Grant is _____ in Grant's tomb." },
|
|
||||||
hasEmbeddedAnswers: true,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'buried' },
|
|
||||||
feedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'entombed' },
|
|
||||||
feedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'living' },
|
|
||||||
feedback: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'TF',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'moodle', text: "Grant is buried in Grant's tomb." },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
isTrue: false,
|
|
||||||
falseFeedback: null,
|
|
||||||
trueFeedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Short',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'moodle', text: "Who's buried in Grant's tomb?" },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'no one " has got me' },
|
|
||||||
feedback: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'nobody' },
|
|
||||||
feedback: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Numerical',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'moodle', text: 'When was Ulysses S. Grant born?' },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: {
|
|
||||||
type: 'range',
|
|
||||||
number: 1822,
|
|
||||||
range: 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Matching',
|
|
||||||
title: null,
|
|
||||||
stem: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'Match the following countries with their corresponding capitals.'
|
|
||||||
},
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
matchPairs: [
|
|
||||||
{
|
|
||||||
subquestion: { format: 'moodle', text: 'Canada' },
|
|
||||||
subanswer: 'Ottawa'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subquestion: { format: 'moodle', text: 'Italy' },
|
|
||||||
subanswer: 'Rome'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subquestion: { format: 'moodle', text: 'Japan' },
|
|
||||||
subanswer: 'Tokyo'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'MC',
|
|
||||||
title: "Grant's Tomb",
|
|
||||||
stem: { format: 'moodle', text: "Grant is _____ in Grant's tomb." },
|
|
||||||
hasEmbeddedAnswers: true,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'buried' },
|
|
||||||
feedback: { format: 'moodle', text: 'No one is buried there.' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'entombed' },
|
|
||||||
feedback: { format: 'moodle', text: 'Right answer!' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'living' },
|
|
||||||
feedback: { format: 'moodle', text: 'We hope not!' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'MC',
|
|
||||||
title: null,
|
|
||||||
stem: { format: 'moodle', text: 'Difficult multiple choice question.' },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'wrong answer' },
|
|
||||||
feedback: { format: 'moodle', text: 'comment on wrong answer' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: false,
|
|
||||||
weight: 50,
|
|
||||||
text: { format: 'moodle', text: 'half credit answer' },
|
|
||||||
feedback: { format: 'moodle', text: 'comment on answer' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'full credit answer' },
|
|
||||||
feedback: { format: 'moodle', text: 'well done!' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Short',
|
|
||||||
title: "Jesus' hometown (Short answer ex.)",
|
|
||||||
stem: { format: 'moodle', text: 'Jesus Christ was from _____ .' },
|
|
||||||
hasEmbeddedAnswers: true,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: { format: 'moodle', text: 'Nazareth' },
|
|
||||||
feedback: { format: 'moodle', text: "Yes! That's right!" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: 75,
|
|
||||||
text: { format: 'moodle', text: 'Nazereth' },
|
|
||||||
feedback: { format: 'moodle', text: 'Right, but misspelled.' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: 25,
|
|
||||||
text: { format: 'moodle', text: 'Bethlehem' },
|
|
||||||
feedback: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'He was born here, but not raised here.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Numerical',
|
|
||||||
title: 'Numerical example',
|
|
||||||
stem: { format: 'moodle', text: 'When was Ulysses S. Grant born?' },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: null,
|
|
||||||
text: {
|
|
||||||
type: 'range',
|
|
||||||
number: 1822,
|
|
||||||
range: 0
|
|
||||||
},
|
|
||||||
feedback: { format: 'moodle', text: 'Correct! 100% credit' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCorrect: true,
|
|
||||||
weight: 50,
|
|
||||||
text: {
|
|
||||||
type: 'range',
|
|
||||||
number: 1822,
|
|
||||||
range: 2
|
|
||||||
},
|
|
||||||
feedback: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'He was born in 1822. You get 50% credit for being close.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Essay',
|
|
||||||
title: 'Essay Example',
|
|
||||||
stem: { format: 'moodle', text: 'This is an essay.' },
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const items = multiple.map((item) => Template(item, { theme: 'dark' })).join('');
|
const items = multiple.map((item) => Template(item, { theme: 'dark' })).join('');
|
||||||
const errorItemDark = ErrorTemplate('Hello');
|
const errorItemDark = ErrorTemplate('Hello');
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { TemplateOptions, Category as CategoryType } from './types';
|
|
||||||
import QuestionContainer from './QuestionContainer';
|
|
||||||
import Title from './Title';
|
|
||||||
|
|
||||||
type CategoryOptions = TemplateOptions & CategoryType;
|
|
||||||
|
|
||||||
export default function Category({ title }: CategoryOptions): string {
|
|
||||||
return `${QuestionContainer({
|
|
||||||
children: Title({
|
|
||||||
type: 'Catégorie',
|
|
||||||
title: title
|
|
||||||
})
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { TemplateOptions } from './types';
|
||||||
|
import QuestionContainer from './QuestionContainerTemplate';
|
||||||
|
import Title from './TitleTemplate';
|
||||||
|
import { Category } from 'gift-pegjs';
|
||||||
|
|
||||||
|
type CategoryOptions = TemplateOptions & Category;
|
||||||
|
|
||||||
|
export default function CategoryTemplate( { title }: CategoryOptions): string {
|
||||||
|
return `${QuestionContainer({
|
||||||
|
children: Title({
|
||||||
|
type: 'Catégorie',
|
||||||
|
title: title
|
||||||
|
})
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { TemplateOptions } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import QuestionContainer from './QuestionContainer';
|
import QuestionContainer from './QuestionContainerTemplate';
|
||||||
import Title from './Title';
|
import Title from './TitleTemplate';
|
||||||
import { textType } from './TextType';
|
import { textType } from './TextTypeTemplate';
|
||||||
import { ParagraphStyle } from '../constants';
|
import { ParagraphStyle } from '../constants';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
import { Description } from 'gift-pegjs';
|
import { Description } from 'gift-pegjs';
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { TemplateOptions, Essay as EssayType } from './types';
|
|
||||||
import QuestionContainer from './QuestionContainer';
|
|
||||||
import Title from './Title';
|
|
||||||
import textType from './TextType';
|
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
|
||||||
import { ParagraphStyle, TextAreaStyle } from '../constants';
|
|
||||||
import { state } from '.';
|
|
||||||
|
|
||||||
type EssayOptions = TemplateOptions & EssayType;
|
|
||||||
|
|
||||||
export default function Essay({ title, stem, globalFeedback }: EssayOptions): string {
|
|
||||||
return `${QuestionContainer({
|
|
||||||
children: [
|
|
||||||
Title({
|
|
||||||
type: 'Développement',
|
|
||||||
title: title
|
|
||||||
}),
|
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
`<textarea class="gift-textarea" style="${TextAreaStyle(
|
|
||||||
state.theme
|
|
||||||
)}" placeholder="Entrez votre réponse ici..."></textarea>`,
|
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
|
||||||
]
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
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 { state } from '.';
|
||||||
|
import { EssayQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
|
type EssayOptions = TemplateOptions & EssayQuestion;
|
||||||
|
|
||||||
|
export default function EssayTemplate({ title, formattedStem, formattedGlobalFeedback }: EssayOptions): string {
|
||||||
|
return `${QuestionContainer({
|
||||||
|
children: [
|
||||||
|
Title({
|
||||||
|
type: 'Développement',
|
||||||
|
title: title
|
||||||
|
}),
|
||||||
|
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||||
|
`<textarea class="gift-textarea" style="${TextAreaStyle(
|
||||||
|
state.theme
|
||||||
|
)}" placeholder="Entrez votre réponse ici..."></textarea>`,
|
||||||
|
(formattedGlobalFeedback && formattedGlobalFeedback.text) ?
|
||||||
|
GlobalFeedbackTemplate(formattedGlobalFeedback) : ``
|
||||||
|
]
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import { TemplateOptions, Question } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import textType from './TextType';
|
import {textType} from './TextTypeTemplate';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
import { theme } from '../constants';
|
import { theme } from '../constants';
|
||||||
|
import { TextFormat } from 'gift-pegjs';
|
||||||
|
|
||||||
interface GlobalFeedbackOptions extends TemplateOptions {
|
type GlobalFeedbackOptions = TemplateOptions & TextFormat;
|
||||||
feedback: Question['globalFeedback'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function GlobalFeedback({ feedback }: GlobalFeedbackOptions): string {
|
export default function GlobalFeedbackTemplate({ format, text }: GlobalFeedbackOptions): string {
|
||||||
const Container = `
|
const Container = `
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
|
@ -19,9 +18,9 @@ export default function GlobalFeedback({ feedback }: GlobalFeedbackOptions): str
|
||||||
box-shadow: 0px 2px 5px ${theme(state.theme, 'gray400', 'black800')};
|
box-shadow: 0px 2px 5px ${theme(state.theme, 'gray400', 'black800')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return feedback !== null
|
return (format && text)
|
||||||
? `<div style="${Container}">
|
? `<div style="${Container}">
|
||||||
<p>${textType({ text: feedback })}</p>
|
<p>${textType({format: format, text: text})}</p>
|
||||||
</div>`
|
</div>`
|
||||||
: ``;
|
: ``;
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
import { TemplateOptions, Matching as MatchingType } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import QuestionContainer from './QuestionContainer';
|
import QuestionContainer from './QuestionContainerTemplate';
|
||||||
import Title from './Title';
|
import Title from './TitleTemplate';
|
||||||
import textType from './TextType';
|
import {textType} from './TextTypeTemplate';
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||||
import { ParagraphStyle, SelectStyle } from '../constants';
|
import { ParagraphStyle, SelectStyle } from '../constants';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
|
import { MatchingQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
type MatchingOptions = TemplateOptions & MatchingType;
|
type MatchingOptions = TemplateOptions & MatchingQuestion;
|
||||||
|
|
||||||
interface MatchAnswerOptions extends TemplateOptions {
|
interface MatchAnswerOptions extends TemplateOptions {
|
||||||
choices: MatchingType['matchPairs'];
|
choices: MatchingQuestion['matchPairs'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Matching({
|
export default function MatchingTemplate({
|
||||||
title,
|
title,
|
||||||
stem,
|
formattedStem,
|
||||||
matchPairs,
|
matchPairs,
|
||||||
globalFeedback
|
formattedGlobalFeedback
|
||||||
}: MatchingOptions): string {
|
}: MatchingOptions): string {
|
||||||
return `${QuestionContainer({
|
return `${QuestionContainer({
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -24,11 +25,9 @@ export default function Matching({
|
||||||
type: 'Appariement',
|
type: 'Appariement',
|
||||||
title: title
|
title: title
|
||||||
}),
|
}),
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
MatchAnswers({ choices: matchPairs }),
|
MatchAnswers({ choices: matchPairs }),
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||||
]
|
]
|
||||||
})}`;
|
})}`;
|
||||||
}
|
}
|
||||||
|
|
@ -64,10 +63,10 @@ function MatchAnswers({ choices }: MatchAnswerOptions): string {
|
||||||
const uniqueMatchOptions = Array.from(new Set(choices.map(({ subanswer }) => subanswer)));
|
const uniqueMatchOptions = Array.from(new Set(choices.map(({ subanswer }) => subanswer)));
|
||||||
|
|
||||||
const result = choices
|
const result = choices
|
||||||
.map(({ subquestion }) => {
|
.map(({ formattedSubquestion }) => {
|
||||||
return `
|
return `
|
||||||
<div style="${OptionTable} ${ParagraphStyle(state.theme)}">
|
<div style="${OptionTable} ${ParagraphStyle(state.theme)}">
|
||||||
${textType({ text: subquestion })}
|
${textType(formattedSubquestion)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<select class="gift-select" style="${SelectStyle(state.theme)} ${Dropdown}">
|
<select class="gift-select" style="${SelectStyle(state.theme)} ${Dropdown}">
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import { TemplateOptions, MultipleChoice as MultipleChoiceType } from './types';
|
|
||||||
import QuestionContainer from './QuestionContainer';
|
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
|
||||||
import Title from './Title';
|
|
||||||
import textType from './TextType';
|
|
||||||
import MultipleChoiceAnswers from './MultipleChoiceAnswers';
|
|
||||||
import { ParagraphStyle } from '../constants';
|
|
||||||
import { state } from '.';
|
|
||||||
|
|
||||||
type MultipleChoiceOptions = TemplateOptions & MultipleChoiceType;
|
|
||||||
|
|
||||||
export default function MultipleChoice({
|
|
||||||
title,
|
|
||||||
stem,
|
|
||||||
choices,
|
|
||||||
globalFeedback
|
|
||||||
}: MultipleChoiceOptions): string {
|
|
||||||
return `${QuestionContainer({
|
|
||||||
children: [
|
|
||||||
Title({
|
|
||||||
type: 'Choix multiple',
|
|
||||||
title: title
|
|
||||||
}),
|
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
MultipleChoiceAnswers({ choices: choices }),
|
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
|
||||||
]
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { TemplateOptions, TextFormat, Choice, MultipleChoice as MultipleChoiceType } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import textType from './TextType';
|
import {textType} from './TextTypeTemplate';
|
||||||
import AnswerIcon from './AnswerIcon';
|
import AnswerIcon from './AnswerIconTemplate';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
import { ParagraphStyle, theme } from '../constants';
|
import { ParagraphStyle, theme } from '../constants';
|
||||||
|
import { MultipleChoiceQuestion, TextChoice } from 'gift-pegjs';
|
||||||
|
|
||||||
type MultipleChoiceAnswerOptions = TemplateOptions & Pick<MultipleChoiceType, 'choices'>;
|
type MultipleChoiceAnswerOptions = TemplateOptions & Pick<MultipleChoiceQuestion, 'choices'>;
|
||||||
|
|
||||||
type AnswerFeedbackOptions = TemplateOptions & Pick<Choice, 'feedback'>;
|
type AnswerFeedbackOptions = TemplateOptions & Pick<TextChoice, 'formattedFeedback'>;
|
||||||
|
|
||||||
interface AnswerWeightOptions extends TemplateOptions {
|
interface AnswerWeightOptions extends TemplateOptions {
|
||||||
weight: Choice['weight'];
|
weight: TextChoice['weight'];
|
||||||
correct: Choice['isCorrect'];
|
correct: TextChoice['isCorrect'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerOptions) {
|
export default function MultipleChoiceAnswersTemplate({ choices }: MultipleChoiceAnswerOptions) {
|
||||||
const id = `id${nanoid(8)}`;
|
const id = `id${nanoid(8)}`;
|
||||||
|
|
||||||
const isMultipleAnswer = choices.filter(({ isCorrect }) => isCorrect === true).length === 0;
|
const isMultipleAnswer = choices.filter(({ isCorrect }) => isCorrect === true).length === 0;
|
||||||
|
|
@ -23,7 +24,7 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
||||||
isMultipleAnswer ? ` ou plusieurs` : ``
|
isMultipleAnswer ? ` ou plusieurs` : ``
|
||||||
}:</span>`;
|
}:</span>`;
|
||||||
const result = choices
|
const result = choices
|
||||||
.map(({ weight, isCorrect, text, feedback }) => {
|
.map(({ weight, isCorrect, formattedText, formattedFeedback }) => {
|
||||||
const CustomLabel = `
|
const CustomLabel = `
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.2em 0 0.2em 0;
|
padding: 0.2em 0 0.2em 0;
|
||||||
|
|
@ -31,7 +32,7 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
||||||
|
|
||||||
const inputId = `id${nanoid(6)}`;
|
const inputId = `id${nanoid(6)}`;
|
||||||
|
|
||||||
const isPositiveWeight = weight !== null && weight > 0;
|
const isPositiveWeight = (weight != undefined) && (weight > 0);
|
||||||
const isCorrectOption = isMultipleAnswer ? isPositiveWeight : isCorrect;
|
const isCorrectOption = isMultipleAnswer ? isPositiveWeight : isCorrect;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
|
@ -41,10 +42,10 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
||||||
}" id="${inputId}" name="${id}">
|
}" id="${inputId}" name="${id}">
|
||||||
${AnswerWeight({ correct: isCorrectOption, weight: weight })}
|
${AnswerWeight({ correct: isCorrectOption, weight: weight })}
|
||||||
<label style="${CustomLabel} ${ParagraphStyle(state.theme)}" for="${inputId}">
|
<label style="${CustomLabel} ${ParagraphStyle(state.theme)}" for="${inputId}">
|
||||||
${textType({ text: text as TextFormat })}
|
${textType(formattedText)}
|
||||||
</label>
|
</label>
|
||||||
${AnswerIcon({ correct: isCorrectOption })}
|
${AnswerIcon({ correct: isCorrectOption })}
|
||||||
${AnswerFeedback({ feedback: feedback })}
|
${AnswerFeedback({ formattedFeedback: formattedFeedback })}
|
||||||
</input>
|
</input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -80,10 +81,10 @@ function AnswerWeight({ weight, correct }: AnswerWeightOptions): string {
|
||||||
: ``;
|
: ``;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AnswerFeedback({ feedback }: AnswerFeedbackOptions): string {
|
function AnswerFeedback({ formattedFeedback }: AnswerFeedbackOptions): string {
|
||||||
const Container = `
|
const Container = `
|
||||||
color: ${theme(state.theme, 'teal700', 'gray700')};
|
color: ${theme(state.theme, 'teal700', 'gray700')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return feedback ? `<span style="${Container}">${textType({ text: feedback })}</span>` : ``;
|
return formattedFeedback ? `<span style="${Container}">${textType(formattedFeedback)}</span>` : ``;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
type MultipleChoiceOptions = TemplateOptions & MultipleChoiceQuestion;
|
||||||
|
|
||||||
|
export default function MultipleChoiceTemplate({
|
||||||
|
title,
|
||||||
|
formattedStem,
|
||||||
|
choices,
|
||||||
|
formattedGlobalFeedback
|
||||||
|
}: MultipleChoiceOptions): string {
|
||||||
|
return `${QuestionContainer({
|
||||||
|
children: [
|
||||||
|
Title({
|
||||||
|
type: 'Choix multiple',
|
||||||
|
title: title
|
||||||
|
}),
|
||||||
|
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||||
|
MultipleChoiceAnswers({ choices: choices }),
|
||||||
|
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||||
|
]
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import { TemplateOptions, Numerical as NumericalType, NumericalFormat } from './types';
|
|
||||||
import QuestionContainer from './QuestionContainer';
|
|
||||||
import Title from './Title';
|
|
||||||
import textType from './TextType';
|
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
|
||||||
import { ParagraphStyle, InputStyle } from '../constants';
|
|
||||||
import { state } from '.';
|
|
||||||
|
|
||||||
type NumericalOptions = TemplateOptions & NumericalType;
|
|
||||||
type NumericalAnswerOptions = TemplateOptions & Pick<NumericalType, 'choices'>;
|
|
||||||
|
|
||||||
export default function Numerical({
|
|
||||||
title,
|
|
||||||
stem,
|
|
||||||
choices,
|
|
||||||
globalFeedback
|
|
||||||
}: NumericalOptions): string {
|
|
||||||
return `${QuestionContainer({
|
|
||||||
children: [
|
|
||||||
Title({
|
|
||||||
type: 'Numérique',
|
|
||||||
title: title
|
|
||||||
}),
|
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
NumericalAnswers({ choices: choices }),
|
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
|
||||||
]
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function NumericalAnswers({ choices }: NumericalAnswerOptions): string {
|
|
||||||
const placeholder = Array.isArray(choices)
|
|
||||||
? choices.map(({ text }) => Answer(text)).join(', ')
|
|
||||||
: Answer(choices);
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div>
|
|
||||||
<span style="${ParagraphStyle(
|
|
||||||
state.theme
|
|
||||||
)}">Réponse: </span><input class="gift-input" type="text" style="${InputStyle(
|
|
||||||
state.theme
|
|
||||||
)}" placeholder="${placeholder}">
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Answer({ type, number, range, numberLow, numberHigh }: NumericalFormat): string {
|
|
||||||
switch (type) {
|
|
||||||
case 'simple':
|
|
||||||
return `${number}`;
|
|
||||||
case 'range':
|
|
||||||
return `${number} ± ${range}`;
|
|
||||||
case 'high-low':
|
|
||||||
return `${numberLow} - ${numberHigh}`;
|
|
||||||
default:
|
|
||||||
return ``;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
type NumericalOptions = TemplateOptions & NumericalQuestion;
|
||||||
|
type NumericalAnswerOptions = TemplateOptions & Pick<NumericalQuestion, 'choices'>;
|
||||||
|
|
||||||
|
export default function NumericalTemplate({
|
||||||
|
title,
|
||||||
|
formattedStem,
|
||||||
|
choices,
|
||||||
|
formattedGlobalFeedback
|
||||||
|
}: NumericalOptions): string {
|
||||||
|
return `${QuestionContainer({
|
||||||
|
children: [
|
||||||
|
Title({
|
||||||
|
type: 'Numérique',
|
||||||
|
title: title
|
||||||
|
}),
|
||||||
|
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||||
|
NumericalAnswers({ choices: choices }),
|
||||||
|
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||||
|
]
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function NumericalAnswers({ choices }: NumericalAnswerOptions): string {
|
||||||
|
const placeholder = choices.length > 1
|
||||||
|
? choices.map(choice => {Answer(choice)}).join(', ')
|
||||||
|
: Answer(choices[0]);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div>
|
||||||
|
<span style="${ParagraphStyle(
|
||||||
|
state.theme
|
||||||
|
)}">Réponse: </span><input class="gift-input" type="text" style="${InputStyle(
|
||||||
|
state.theme
|
||||||
|
)}" placeholder="${placeholder}">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Answer(choice: NumericalAnswer): string {
|
||||||
|
switch (true) {
|
||||||
|
case isSimpleNumericalAnswer(choice):
|
||||||
|
return `${choice.number}`;
|
||||||
|
case isRangeNumericalAnswer(choice):
|
||||||
|
return `${choice.number} ± ${choice.range}`;
|
||||||
|
case isHighLowNumericalAnswer(choice):
|
||||||
|
return `${choice.numberLow}..${choice.numberHigh}`;
|
||||||
|
default:
|
||||||
|
return ``;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
import { TemplateOptions, ShortAnswer as ShortAnswerType, TextFormat } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import QuestionContainer from './QuestionContainer';
|
import QuestionContainer from './QuestionContainerTemplate';
|
||||||
import Title from './Title';
|
import Title from './TitleTemplate';
|
||||||
import textType from './TextType';
|
import {textType} from './TextTypeTemplate';
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||||
import { ParagraphStyle, InputStyle } from '../constants';
|
import { ParagraphStyle, InputStyle } from '../constants';
|
||||||
import { state } from './index';
|
import { state } from './index';
|
||||||
|
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||||
|
|
||||||
type ShortAnswerOptions = TemplateOptions & ShortAnswerType;
|
type ShortAnswerOptions = TemplateOptions & ShortAnswerQuestion;
|
||||||
type AnswerOptions = TemplateOptions & Pick<ShortAnswerType, 'choices'>;
|
type AnswerOptions = TemplateOptions & Pick<ShortAnswerQuestion, 'choices'>;
|
||||||
|
|
||||||
export default function ShortAnswer({
|
export default function ShortAnswerTemplate({
|
||||||
title,
|
title,
|
||||||
stem,
|
formattedStem,
|
||||||
choices,
|
choices,
|
||||||
globalFeedback
|
formattedGlobalFeedback
|
||||||
}: ShortAnswerOptions): string {
|
}: ShortAnswerOptions): string {
|
||||||
return `${QuestionContainer({
|
return `${QuestionContainer({
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -21,18 +22,16 @@ export default function ShortAnswer({
|
||||||
type: 'Réponse courte',
|
type: 'Réponse courte',
|
||||||
title: title
|
title: title
|
||||||
}),
|
}),
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
Answers({ choices: choices }),
|
Answers({ choices: choices }),
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||||
]
|
]
|
||||||
})}`;
|
})}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Answers({ choices }: AnswerOptions): string {
|
function Answers({ choices }: AnswerOptions): string {
|
||||||
const placeholder = choices
|
const placeholder = choices
|
||||||
.map(({ text }) => textType({ text: text as TextFormat }))
|
.map(({ text }) => textType({ format: '', text: text }))
|
||||||
.join(', ');
|
.join(', ');
|
||||||
return `
|
return `
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -29,6 +29,7 @@ export function textType(formattedText: TextFormat): string {
|
||||||
const formatText = formatLatex(formattedText.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
|
const formatText = formatLatex(formattedText.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
|
||||||
let parsedText = '';
|
let parsedText = '';
|
||||||
switch (formattedText.format) {
|
switch (formattedText.format) {
|
||||||
|
case '':
|
||||||
case 'moodle':
|
case 'moodle':
|
||||||
case 'plain':
|
case 'plain':
|
||||||
// Replace newlines with <br> tags
|
// Replace newlines with <br> tags
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { TemplateOptions, Question } from './types';
|
import { TemplateOptions } from './types';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
import { theme } from '../constants';
|
import { theme } from '../constants';
|
||||||
|
import { Question } from 'gift-pegjs';
|
||||||
|
|
||||||
// Type is string to allow for custom question type text (e,g, "Multiple Choice")
|
// Type is string to allow for custom question type text (e,g, "Multiple Choice")
|
||||||
interface TitleOptions extends TemplateOptions {
|
interface TitleOptions extends TemplateOptions {
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import { TemplateOptions, TextChoice, TrueFalse as TrueFalseType } from './types';
|
|
||||||
import QuestionContainer from './QuestionContainer';
|
|
||||||
import textType from './TextType';
|
|
||||||
import GlobalFeedback from './GlobalFeedback';
|
|
||||||
import MultipleChoiceAnswers from './MultipleChoiceAnswers';
|
|
||||||
import Title from './Title';
|
|
||||||
import { ParagraphStyle } from '../constants';
|
|
||||||
import { state } from '.';
|
|
||||||
|
|
||||||
type TrueFalseOptions = TemplateOptions & TrueFalseType;
|
|
||||||
|
|
||||||
export default function TrueFalse({
|
|
||||||
title,
|
|
||||||
isTrue,
|
|
||||||
stem,
|
|
||||||
trueFeedback: trueFeedback,
|
|
||||||
falseFeedback: falseFeedback,
|
|
||||||
globalFeedback
|
|
||||||
}: TrueFalseOptions): string {
|
|
||||||
const choices: TextChoice[] = [
|
|
||||||
{
|
|
||||||
text: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'Vrai'
|
|
||||||
},
|
|
||||||
isCorrect: isTrue,
|
|
||||||
weight: null,
|
|
||||||
feedback: isTrue ? trueFeedback : falseFeedback
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: {
|
|
||||||
format: 'moodle',
|
|
||||||
text: 'Faux'
|
|
||||||
},
|
|
||||||
isCorrect: !isTrue,
|
|
||||||
weight: null,
|
|
||||||
feedback: !isTrue ? trueFeedback : falseFeedback
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return `${QuestionContainer({
|
|
||||||
children: [
|
|
||||||
Title({
|
|
||||||
type: 'Vrai/Faux',
|
|
||||||
title: title
|
|
||||||
}),
|
|
||||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
|
||||||
text: stem
|
|
||||||
})}</p>`,
|
|
||||||
MultipleChoiceAnswers({ choices: choices }),
|
|
||||||
GlobalFeedback({ feedback: globalFeedback })
|
|
||||||
]
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
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 DOMPurify from 'dompurify';
|
||||||
|
|
||||||
|
type TrueFalseOptions = TemplateOptions & TrueFalseQuestion;
|
||||||
|
|
||||||
|
export default function TrueFalseTemplate({
|
||||||
|
isTrue,
|
||||||
|
title,
|
||||||
|
formattedStem,
|
||||||
|
trueFormattedFeedback, falseFormattedFeedback,
|
||||||
|
formattedGlobalFeedback
|
||||||
|
}: TrueFalseOptions): string {
|
||||||
|
const choices: TextChoice[] = [
|
||||||
|
{
|
||||||
|
formattedText: {
|
||||||
|
format: 'moodle',
|
||||||
|
text: 'Vrai'
|
||||||
|
},
|
||||||
|
isCorrect: isTrue,
|
||||||
|
formattedFeedback: trueFormattedFeedback
|
||||||
|
},
|
||||||
|
{
|
||||||
|
formattedText: {
|
||||||
|
format: 'moodle',
|
||||||
|
text: 'Faux'
|
||||||
|
},
|
||||||
|
isCorrect: !isTrue,
|
||||||
|
formattedFeedback: falseFormattedFeedback
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return `${QuestionContainer({
|
||||||
|
children: [
|
||||||
|
Title({
|
||||||
|
type: 'Vrai/Faux',
|
||||||
|
title: title
|
||||||
|
}),
|
||||||
|
`<div dangerouslySetInnerHTML={{ __html: ${DOMPurify.sanitize(textType(formattedStem))} }} />`,
|
||||||
|
MultipleChoiceAnswersTemplate({ choices: choices }),
|
||||||
|
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ``
|
||||||
|
]
|
||||||
|
})}`;
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,25 @@
|
||||||
import Category from './Category';
|
|
||||||
import DescriptionTemplate from './Description';
|
|
||||||
import Essay from './Essay';
|
|
||||||
import Matching from './Matching';
|
|
||||||
import MultipleChoice from './MultipleChoice';
|
|
||||||
import Numerical from './Numerical';
|
|
||||||
import ShortAnswer from './ShortAnswer';
|
|
||||||
import TrueFalse from './TrueFalse';
|
|
||||||
import Error from './Error';
|
|
||||||
import {
|
import {
|
||||||
GIFTQuestion,
|
ParsedGIFTQuestion as GIFTQuestion,
|
||||||
// Category as CategoryType,
|
// Category as CategoryType,
|
||||||
// Description as DescriptionType,
|
// Description as DescriptionType,
|
||||||
MultipleChoice as MultipleChoiceType,
|
MultipleChoiceQuestion as MultipleChoiceType,
|
||||||
Numerical as NumericalType,
|
NumericalQuestion as NumericalType,
|
||||||
ShortAnswer as ShortAnswerType,
|
ShortAnswerQuestion as ShortAnswerType,
|
||||||
// Essay as EssayType,
|
// Essay as EssayType,
|
||||||
TrueFalse as TrueFalseType,
|
TrueFalseQuestion as TrueFalseType,
|
||||||
Matching as MatchingType,
|
// MatchingQuestion as MatchingType,
|
||||||
DisplayOptions
|
} from 'gift-pegjs';
|
||||||
} from './types';
|
import { DisplayOptions } from './types';
|
||||||
|
import DescriptionTemplate from './DescriptionTemplate';
|
||||||
|
import EssayTemplate from './EssayTemplate';
|
||||||
|
import MatchingTemplate from './MatchingTemplate';
|
||||||
|
import MultipleChoiceTemplate from './MultipleChoiceTemplate';
|
||||||
|
import NumericalTemplate from './NumericalTemplate';
|
||||||
|
import ShortAnswerTemplate from './ShortAnswerTemplate';
|
||||||
|
import TrueFalseTemplate from './TrueFalseTemplate';
|
||||||
|
import Error from './ErrorTemplate';
|
||||||
|
import CategoryTemplate from './CategoryTemplate';
|
||||||
|
|
||||||
export const state: DisplayOptions = { preview: true, theme: 'light' };
|
export const state: DisplayOptions = { preview: true, theme: 'light' };
|
||||||
|
|
||||||
|
|
@ -37,21 +38,21 @@ export default function Template(
|
||||||
// ...(keys as DescriptionType)
|
// ...(keys as DescriptionType)
|
||||||
// });
|
// });
|
||||||
case 'MC':
|
case 'MC':
|
||||||
return MultipleChoice({
|
return MultipleChoiceTemplate({
|
||||||
...(keys as MultipleChoiceType)
|
...(keys as MultipleChoiceType)
|
||||||
});
|
});
|
||||||
case 'Numerical':
|
case 'Numerical':
|
||||||
return Numerical({ ...(keys as NumericalType) });
|
return NumericalTemplate({ ...(keys as NumericalType) });
|
||||||
case 'Short':
|
case 'Short':
|
||||||
return ShortAnswer({
|
return ShortAnswerTemplate({
|
||||||
...(keys as ShortAnswerType)
|
...(keys as ShortAnswerType)
|
||||||
});
|
});
|
||||||
// case 'Essay':
|
// case 'Essay':
|
||||||
// return Essay({ ...(keys as EssayType) });
|
// return Essay({ ...(keys as EssayType) });
|
||||||
case 'TF':
|
case 'TF':
|
||||||
return TrueFalse({ ...(keys as TrueFalseType) });
|
return TrueFalseTemplate({ ...(keys as TrueFalseType) });
|
||||||
case 'Matching':
|
// case 'Matching':
|
||||||
return Matching({ ...(keys as MatchingType) });
|
// return Matching({ ...(keys as MatchingType) });
|
||||||
default:
|
default:
|
||||||
// TODO: throw error for unsupported question types?
|
// TODO: throw error for unsupported question types?
|
||||||
// throw new Error(`Unsupported question type: ${type}`);
|
// throw new Error(`Unsupported question type: ${type}`);
|
||||||
|
|
@ -66,13 +67,13 @@ export function ErrorTemplate(text: string, options?: Partial<DisplayOptions>):
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Category,
|
CategoryTemplate,
|
||||||
DescriptionTemplate as Description,
|
DescriptionTemplate as Description,
|
||||||
Essay,
|
EssayTemplate as Essay,
|
||||||
Matching,
|
MatchingTemplate as Matching,
|
||||||
MultipleChoice,
|
MultipleChoiceTemplate as MultipleChoice,
|
||||||
Numerical,
|
NumericalTemplate as Numerical,
|
||||||
ShortAnswer,
|
ShortAnswerTemplate as ShortAnswer,
|
||||||
TrueFalse,
|
TrueFalseTemplate as TrueFalse,
|
||||||
Error
|
Error
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import {
|
||||||
TableRow
|
TableRow
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { StudentType } from '../../Types/StudentType';
|
import { StudentType } from '../../Types/StudentType';
|
||||||
import { formatLatex } from '../GiftTemplate/templates/TextType';
|
import { formatLatex } from '../GiftTemplate/templates/TextTypeTemplate';
|
||||||
|
|
||||||
interface LiveResultsProps {
|
interface LiveResultsProps {
|
||||||
socket: Socket | null;
|
socket: Socket | null;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-container">
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({text: question.formattedStem})) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||||
</div>
|
</div>
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
{question.choices.map((choice, i) => {
|
{question.choices.map((choice, i) => {
|
||||||
|
|
@ -48,13 +48,13 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
(choice.isCorrect ? '✅' : '❌')}
|
(choice.isCorrect ? '✅' : '❌')}
|
||||||
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||||
<div className={`answer-text ${selected}`}>
|
<div className={`answer-text ${selected}`}>
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: choice.formattedText })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedText)) }} />
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
{choice.formattedFeedback && showAnswer && (
|
{choice.formattedFeedback && showAnswer && (
|
||||||
<div className="feedback-container mb-1 mt-1/2">
|
<div className="feedback-container mb-1 mt-1/2">
|
||||||
{choice.isCorrect ? '✅' : '❌'}
|
{choice.isCorrect ? '✅' : '❌'}
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: choice.formattedFeedback })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedFeedback)) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -63,7 +63,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && showAnswer && (
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
<div className="global-feedback mb-2">
|
<div className="global-feedback mb-2">
|
||||||
<p>${textType({ text: question.formattedGlobalFeedback })}</p>
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
import { NumericalQuestion, SimpleNumericalAnswer, RangeNumericalAnswer, HighLowNumericalAnswer } from 'gift-pegjs';
|
||||||
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
@ -41,13 +41,13 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
<div>
|
<div>
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedStem })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
<div className="correct-answer-text mb-2">{correctAnswer}</div>
|
<div className="correct-answer-text mb-2">{correctAnswer}</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedGlobalFeedback })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||||
</div>}
|
</div>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -65,7 +65,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && showAnswer && (
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
<div className="global-feedback mb-2">
|
<div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedGlobalFeedback })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{handleOnSubmitAnswer && (
|
{handleOnSubmitAnswer && (
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// Question;tsx
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Question } from 'gift-pegjs';
|
import { Question } from 'gift-pegjs';
|
||||||
|
|
||||||
import TrueFalseQuestion from './TrueFalseQuestion/TrueFalseQuestion';
|
import TrueFalseQuestionDisplay from './TrueFalseQuestionDisplay/TrueFalseQuestionDisplay';
|
||||||
import MultipleChoiceQuestionDisplay from './MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay';
|
import MultipleChoiceQuestionDisplay from './MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay';
|
||||||
import NumericalQuestionDisplay from './NumericalQuestionDisplay/NumericalQuestionDisplay';
|
import NumericalQuestionDisplay from './NumericalQuestionDisplay/NumericalQuestionDisplay';
|
||||||
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
||||||
|
|
@ -27,12 +26,10 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
||||||
switch (question?.type) {
|
switch (question?.type) {
|
||||||
case 'TF':
|
case 'TF':
|
||||||
questionTypeComponent = (
|
questionTypeComponent = (
|
||||||
<TrueFalseQuestion
|
<TrueFalseQuestionDisplay
|
||||||
questionContent={question.formattedStem}
|
question={question}
|
||||||
correctAnswer={question.isTrue}
|
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
globalFeedback={question.formattedGlobalFeedback?.text}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({text: question.formattedStem})) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -30,7 +30,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({text: question.formattedGlobalFeedback})) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||||
</div>}
|
</div>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,32 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import '../questionStyle.css';
|
import '../questionStyle.css';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
import { TrueFalseQuestion } from 'gift-pegjs';
|
||||||
import { TextFormat } from 'gift-pegjs';
|
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
import { textType } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
questionContent: TextFormat;
|
question: TrueFalseQuestion;
|
||||||
correctAnswer: boolean;
|
|
||||||
globalFeedback?: string | undefined;
|
|
||||||
handleOnSubmitAnswer?: (answer: boolean) => void;
|
handleOnSubmitAnswer?: (answer: boolean) => void;
|
||||||
showAnswer?: boolean;
|
showAnswer?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrueFalseQuestion: React.FC<Props> = (props) => {
|
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { questionContent, correctAnswer, showAnswer, handleOnSubmitAnswer, globalFeedback } =
|
const { question, showAnswer, handleOnSubmitAnswer } =
|
||||||
props;
|
props;
|
||||||
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAnswer(undefined);
|
setAnswer(undefined);
|
||||||
}, [questionContent]);
|
}, [question]);
|
||||||
|
|
||||||
const selectedTrue = answer ? 'selected' : '';
|
const selectedTrue = answer ? 'selected' : '';
|
||||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||||
|
const correctAnswer = question.isTrue === answer;
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-container">
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: questionContent })) }} />
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||||
</div>
|
</div>
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -50,8 +49,22 @@ const TrueFalseQuestion: React.FC<Props> = (props) => {
|
||||||
<div className={`answer-text ${selectedFalse}`}>Faux</div>
|
<div className={`answer-text ${selectedFalse}`}>Faux</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{globalFeedback && showAnswer && (
|
{/* selected TRUE, show True feedback if it exists */}
|
||||||
<div className="global-feedback mb-2">{globalFeedback}</div>
|
{showAnswer && answer && question.trueFormattedFeedback && (
|
||||||
|
<div className="true-feedback mb-2">
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(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>
|
||||||
|
)}
|
||||||
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
|
<div className="global-feedback mb-2">
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{!showAnswer && handleOnSubmitAnswer && (
|
{!showAnswer && handleOnSubmitAnswer && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -68,4 +81,4 @@ const TrueFalseQuestion: React.FC<Props> = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TrueFalseQuestion;
|
export default TrueFalseQuestionDisplay;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// StudentModeQuiz.tsx
|
// StudentModeQuiz.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import QuestionComponent from '../Questions/QuestionDisplay';
|
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||||
import { QuestionType } from '../../Types/QuestionType';
|
import { QuestionType } from '../../Types/QuestionType';
|
||||||
// import { QuestionService } from '../../services/QuestionService';
|
// import { QuestionService } from '../../services/QuestionService';
|
||||||
|
|
@ -8,6 +8,7 @@ import { Button } from '@mui/material';
|
||||||
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
||||||
//import { ChevronLeft, ChevronRight } from '@mui/icons-material';
|
//import { ChevronLeft, ChevronRight } from '@mui/icons-material';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
|
import { Question } from 'gift-pegjs';
|
||||||
|
|
||||||
interface StudentModeQuizProps {
|
interface StudentModeQuizProps {
|
||||||
questions: QuestionType[];
|
questions: QuestionType[];
|
||||||
|
|
@ -63,7 +64,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question}
|
question={questionInfos.question as Question}
|
||||||
showAnswer={isAnswerSubmitted}
|
showAnswer={isAnswerSubmitted}
|
||||||
/>
|
/>
|
||||||
<div className="center-h-align mt-1/2">
|
<div className="center-h-align mt-1/2">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
// TeacherModeQuiz.tsx
|
// TeacherModeQuiz.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import QuestionComponent from '../Questions/QuestionDisplay';
|
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||||
|
|
||||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||||
import { QuestionType } from '../../Types/QuestionType';
|
import { QuestionType } from '../../Types/QuestionType';
|
||||||
// import { QuestionService } from '../../services/QuestionService';
|
// import { QuestionService } from '../../services/QuestionService';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
||||||
|
import { Question } from 'gift-pegjs';
|
||||||
|
|
||||||
interface TeacherModeQuizProps {
|
interface TeacherModeQuizProps {
|
||||||
questionInfos: QuestionType;
|
questionInfos: QuestionType;
|
||||||
|
|
@ -63,7 +64,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
) : (
|
) : (
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question}
|
question={questionInfos.question as Question}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -76,7 +77,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
{feedbackMessage}
|
{feedbackMessage}
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question}
|
question={questionInfos.question as Question}
|
||||||
showAnswer={true}
|
showAnswer={true}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { GIFTQuestion, parse } from 'gift-pegjs';
|
import { ParsedGIFTQuestion, BaseQuestion, parse, Question } from 'gift-pegjs';
|
||||||
import { QuestionType } from '../../../Types/QuestionType';
|
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer } from "gift-pegjs/typeGuards";
|
||||||
import LiveResultsComponent from 'src/components/LiveResults/LiveResults';
|
import LiveResultsComponent from 'src/components/LiveResults/LiveResults';
|
||||||
// import { QuestionService } from '../../../services/QuestionService';
|
// import { QuestionService } from '../../../services/QuestionService';
|
||||||
import webSocketService, { AnswerReceptionFromBackendType } from '../../../services/WebsocketService';
|
import webSocketService, { AnswerReceptionFromBackendType } from '../../../services/WebsocketService';
|
||||||
|
|
@ -18,8 +18,9 @@ import { Refresh, Error } from '@mui/icons-material';
|
||||||
import StudentWaitPage from 'src/components/StudentWaitPage/StudentWaitPage';
|
import StudentWaitPage from 'src/components/StudentWaitPage/StudentWaitPage';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
//import QuestionNavigation from 'src/components/QuestionNavigation/QuestionNavigation';
|
//import QuestionNavigation from 'src/components/QuestionNavigation/QuestionNavigation';
|
||||||
import QuestionDisplay from 'src/components/Questions/QuestionDisplay';
|
import QuestionDisplay from 'src/components/QuestionsDisplay/QuestionDisplay';
|
||||||
import ApiService from '../../../services/ApiService';
|
import ApiService from '../../../services/ApiService';
|
||||||
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
|
||||||
const ManageRoom: React.FC = () => {
|
const ManageRoom: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
@ -277,7 +278,7 @@ const ManageRoom: React.FC = () => {
|
||||||
const parsedQuestions = [] as QuestionType[];
|
const parsedQuestions = [] as QuestionType[];
|
||||||
|
|
||||||
quizQuestionArray.forEach((question, index) => {
|
quizQuestionArray.forEach((question, index) => {
|
||||||
parsedQuestions.push({ question: parse(question)[0] });
|
parsedQuestions.push({ question: parse(question)[0] as BaseQuestion });
|
||||||
parsedQuestions[index].question.id = (index + 1).toString();
|
parsedQuestions[index].question.id = (index + 1).toString();
|
||||||
});
|
});
|
||||||
if (parsedQuestions.length === 0) return null;
|
if (parsedQuestions.length === 0) return null;
|
||||||
|
|
@ -347,7 +348,7 @@ const ManageRoom: React.FC = () => {
|
||||||
|
|
||||||
const answerText = answer.toString();
|
const answerText = answer.toString();
|
||||||
if (questionInfo) {
|
if (questionInfo) {
|
||||||
const question = questionInfo.question as GIFTQuestion;
|
const question = questionInfo.question as ParsedGIFTQuestion;
|
||||||
if (question.type === 'TF') {
|
if (question.type === 'TF') {
|
||||||
return (
|
return (
|
||||||
(question.isTrue && answerText == 'true') ||
|
(question.isTrue && answerText == 'true') ||
|
||||||
|
|
@ -355,33 +356,23 @@ const ManageRoom: React.FC = () => {
|
||||||
);
|
);
|
||||||
} else if (question.type === 'MC') {
|
} else if (question.type === 'MC') {
|
||||||
return question.choices.some(
|
return question.choices.some(
|
||||||
(choice) => choice.isCorrect && choice.text.text === answerText
|
(choice) => choice.isCorrect && choice.formattedText.text === answerText
|
||||||
);
|
);
|
||||||
} else if (question.type === 'Numerical') {
|
} else if (question.type === 'Numerical') {
|
||||||
if (question.choices && !Array.isArray(question.choices)) {
|
if (isHighLowNumericalAnswer(question.choices[0])) {
|
||||||
if (
|
const choice = question.choices[0];
|
||||||
question.choices.type === 'high-low' &&
|
|
||||||
question.choices.numberHigh &&
|
|
||||||
question.choices.numberLow
|
|
||||||
) {
|
|
||||||
const answerNumber = parseFloat(answerText);
|
const answerNumber = parseFloat(answerText);
|
||||||
if (!isNaN(answerNumber)) {
|
if (!isNaN(answerNumber)) {
|
||||||
return (
|
return (
|
||||||
answerNumber <= question.choices.numberHigh &&
|
answerNumber <= choice.numberHigh &&
|
||||||
answerNumber >= question.choices.numberLow
|
answerNumber >= choice.numberLow
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (isRangeNumericalAnswer(question.choices[0])) {
|
||||||
if (question.choices && Array.isArray(question.choices)) {
|
|
||||||
if (
|
|
||||||
question.choices[0].text.type === 'range' &&
|
|
||||||
question.choices[0].text.number &&
|
|
||||||
question.choices[0].text.range
|
|
||||||
) {
|
|
||||||
const answerNumber = parseFloat(answerText);
|
const answerNumber = parseFloat(answerText);
|
||||||
const range = question.choices[0].text.range;
|
const range = question.choices[0].range;
|
||||||
const correctAnswer = question.choices[0].text.number;
|
const correctAnswer = question.choices[0].number;
|
||||||
if (!isNaN(answerNumber)) {
|
if (!isNaN(answerNumber)) {
|
||||||
return (
|
return (
|
||||||
answerNumber <= correctAnswer + range &&
|
answerNumber <= correctAnswer + range &&
|
||||||
|
|
@ -389,19 +380,15 @@ const ManageRoom: React.FC = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (isSimpleNumericalAnswer(question.choices[0])) {
|
||||||
question.choices[0].text.type === 'simple' &&
|
|
||||||
question.choices[0].text.number
|
|
||||||
) {
|
|
||||||
const answerNumber = parseFloat(answerText);
|
const answerNumber = parseFloat(answerText);
|
||||||
if (!isNaN(answerNumber)) {
|
if (!isNaN(answerNumber)) {
|
||||||
return answerNumber === question.choices[0].text.number;
|
return answerNumber === question.choices[0].number;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (question.type === 'Short') {
|
} else if (question.type === 'Short') {
|
||||||
return question.choices.some(
|
return question.choices.some(
|
||||||
(choice) => choice.text.text.toUpperCase() === answerText.toUpperCase()
|
(choice) => choice.text.toUpperCase() === answerText.toUpperCase()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -476,7 +463,7 @@ const ManageRoom: React.FC = () => {
|
||||||
{currentQuestion && (
|
{currentQuestion && (
|
||||||
<QuestionDisplay
|
<QuestionDisplay
|
||||||
showAnswer={false}
|
showAnswer={false}
|
||||||
question={currentQuestion?.question}
|
question={currentQuestion?.question as Question}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,19 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
"src/*": ["src/*"]
|
"src/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
|
|
@ -17,7 +22,6 @@
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
|
|
@ -25,6 +29,16 @@
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
// "exclude": [
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
// // "src/components/GiftTemplate/**/*",
|
||||||
|
// // "src/__tests__/components/GiftTemplate/**/*",
|
||||||
|
// ],
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue