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/TextType";
|
||||
import { textType } from "src/components/GiftTemplate/templates/TextTypeTemplate";
|
||||
import { TextFormat } from "gift-pegjs";
|
||||
|
||||
describe('TextType', () => {
|
||||
it('should format text with basic characters correctly', () => {
|
||||
const input: TextFormat = {
|
||||
text: 'Hello, world! 5 > 3, right?',
|
||||
format: 'plain'
|
||||
format: 'moodle'
|
||||
};
|
||||
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', () => {
|
||||
|
|
@ -19,7 +17,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
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', () => {
|
||||
|
|
@ -33,17 +31,17 @@ describe('TextType', () => {
|
|||
// by running the test and copying the "Received string:" in jest output
|
||||
// when it fails (assuming the output is correct)
|
||||
const expectedOutput = '<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8641em;"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>';
|
||||
expect(textType({ text: input })).toContain(expectedOutput);
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with two equations (inline and separate) correctly', () => {
|
||||
const input: TextFormat = {
|
||||
text: '$a + b = c$ ? $$E=mc^2$$',
|
||||
format: 'plain'
|
||||
format: 'moodle'
|
||||
};
|
||||
// hint: katex-display is the class that indicates a separate equation
|
||||
const expectedOutput = '<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>+</mo><mi>b</mi><mo>=</mo><mi>c</mi></mrow><annotation encoding="application/x-tex">a + b = c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">c</span></span></span></span> ? <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8641em;"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>';
|
||||
expect(textType({ text: input })).toContain(expectedOutput);
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with a katex matrix correctly', () => {
|
||||
|
|
@ -53,7 +51,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
const expectedOutput = 'Donnez le déterminant de la matrice suivante.<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow></mrow><annotation encoding="application/x-tex"></annotation></semantics></math></span><span class="katex-html" aria-hidden="true"></span></span>\\begin{pmatrix}<br> a&b \\\\<br> c&d<br>\\end{pmatrix}';
|
||||
expect(textType({ text: input })).toContain(expectedOutput);
|
||||
expect(textType(input)).toContain(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with Markdown correctly', () => {
|
||||
|
|
@ -63,7 +61,7 @@ describe('TextType', () => {
|
|||
};
|
||||
// TODO: investigate why the output has an extra newline
|
||||
const expectedOutput = '<strong>Bold</strong>\n';
|
||||
expect(textType({ text: input })).toBe(expectedOutput);
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format text with HTML correctly', () => {
|
||||
|
|
@ -72,7 +70,7 @@ describe('TextType', () => {
|
|||
format: 'html'
|
||||
};
|
||||
const expectedOutput = '<em>yes</em>';
|
||||
expect(textType({ text: input })).toBe(expectedOutput);
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('should format plain text correctly', () => {
|
||||
|
|
@ -81,7 +79,7 @@ describe('TextType', () => {
|
|||
format: 'plain'
|
||||
};
|
||||
const expectedOutput = 'Just plain text';
|
||||
expect(textType({ text: input })).toBe(expectedOutput);
|
||||
expect(textType(input)).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
// Add more tests for other formats if needed
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
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';
|
||||
|
||||
describe('AnswerIcon', () => {
|
||||
|
|
|
|||
|
|
@ -2,99 +2,100 @@ import React from 'react';
|
|||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
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
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: jest.fn(() => 'mocked-id')
|
||||
}));
|
||||
|
||||
const mockProps: TemplateOptions & MultipleChoiceType = {
|
||||
const mockProps: TemplateOptions & MultipleChoiceQuestion = {
|
||||
type: 'MC',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title',
|
||||
stem: { format: 'plain' , text: 'Sample Stem'},
|
||||
formattedStem: { format: 'plain' , text: 'Sample Stem'},
|
||||
choices: [
|
||||
{ text: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, feedback: { 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 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title',
|
||||
stem: { format: 'plain' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||
formattedStem: { format: 'plain' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||
choices: [
|
||||
{ text: { format: 'plain' , text: 'Choice 1'}, isCorrect: true, feedback: { 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 1'}, isCorrect: true, formattedFeedback: { 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title with Image',
|
||||
stem: { format: 'plain', text: 'Sample Stem with Image' },
|
||||
formattedStem: { format: 'plain', text: 'Sample Stem with Image' },
|
||||
choices: [
|
||||
{ text: { format: 'plain', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: false, feedback: { 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: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ formattedText: { format: 'plain', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title',
|
||||
stem: { format: 'moodle' , text: 'Sample Stem'},
|
||||
formattedStem: { format: 'moodle' , text: 'Sample Stem'},
|
||||
choices: [
|
||||
{ text: { format: 'moodle' , text: 'Choice 1'}, isCorrect: true, feedback: { format: 'plain' , text: 'Correct!'}, weight: 1 },
|
||||
{ text: { format: 'plain', text: 'Choice 2' }, isCorrect: false, feedback: { format: 'plain' , text: 'InCorrect!'}, weight: 1 }
|
||||
{ formattedText: { format: 'moodle' , text: 'Choice 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title',
|
||||
stem: { format: 'html' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||
formattedStem: { format: 'html' , text: '$$\\frac{zzz}{yyy}$$'},
|
||||
choices: [
|
||||
{ text: { format: 'html' , text: 'Choice 1'}, isCorrect: true, feedback: { 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 1'}, isCorrect: true, formattedFeedback: { format: 'plain' , text: 'Correct!'}, 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Title with Image',
|
||||
stem: { format: 'markdown', text: 'Sample Stem with Image' },
|
||||
formattedStem: { format: 'markdown', text: 'Sample Stem with Image' },
|
||||
choices: [
|
||||
{ text: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ text: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, feedback: { 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: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ formattedText: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, 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',
|
||||
hasEmbeddedAnswers: false,
|
||||
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: [
|
||||
{ text: { format: 'markdown', text: 'Choice 1' }, isCorrect: true, feedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ text: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, feedback: { 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: 'Choice 1' }, isCorrect: true, formattedFeedback: { format: 'plain', text: 'Correct!' }, weight: 1 },
|
||||
{ formattedText: { format: 'markdown', text: 'Choice 2' }, isCorrect: false, formattedFeedback: { format: 'plain', text: 'Incorrect!' }, 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', () => {
|
||||
|
|
|
|||
|
|
@ -1,62 +1,40 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import Numerical from 'src/components/GiftTemplate/templates/Numerical';
|
||||
import { TemplateOptions, Numerical as NumericalType } from 'src/components/GiftTemplate/templates/types';
|
||||
import Numerical from 'src/components/GiftTemplate/templates/NumericalTemplate';
|
||||
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||
import { parse, NumericalQuestion } from 'gift-pegjs';
|
||||
|
||||
// Mock the nanoid function
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: jest.fn(() => 'mocked-id')
|
||||
}));
|
||||
|
||||
const plainTextMock: TemplateOptions & NumericalType = {
|
||||
type: 'Numerical',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Numerical Title',
|
||||
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 plainTextMock: TemplateOptions & NumericalQuestion =
|
||||
parse(`
|
||||
::Sample Numerical Title:: Sample Stem {#=42#Correct!=43#Incorrect!####Sample Global Feedback}
|
||||
`)[0] as NumericalQuestion;
|
||||
|
||||
const htmlMock: TemplateOptions & NumericalType = {
|
||||
type: 'Numerical',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Numerical Title',
|
||||
stem: { format: 'html', text: '$$\\frac{zzz}{yyy}$$' },
|
||||
choices: [
|
||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'html', text: 'Correct!' } },
|
||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'html', text: 'Incorrect!' } }
|
||||
],
|
||||
globalFeedback: { format: 'html', text: 'Sample Global Feedback' }
|
||||
};
|
||||
const htmlMock: TemplateOptions & NumericalQuestion =
|
||||
parse(`
|
||||
::Sample Numerical Title::
|
||||
[html]$$\\frac\\{zzz\\}\\{yyy\\}$$ {#
|
||||
=42#Correct
|
||||
=43#Incorrect!
|
||||
####Sample Global Feedback
|
||||
}
|
||||
`)[0] as NumericalQuestion;
|
||||
|
||||
const moodleMock: TemplateOptions & NumericalType = {
|
||||
type: 'Numerical',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Numerical Title',
|
||||
stem: { format: 'moodle', text: 'Sample Stem' },
|
||||
choices: [
|
||||
{ isCorrect: true, weight: 1, text: { type: 'simple', number: 42}, feedback: { format: 'moodle', text: 'Correct!' } },
|
||||
{ isCorrect: false, weight: 1, text: { type: 'simple', number: 43}, feedback: { format: 'moodle', text: 'Incorrect!' } }
|
||||
],
|
||||
globalFeedback: { format: 'moodle', text: 'Sample Global Feedback' }
|
||||
};
|
||||
const moodleMock: TemplateOptions & NumericalQuestion =
|
||||
parse(`
|
||||
::Sample Numerical Title::[moodle]Sample Stem {#=42#Correct!=43#Incorrect!####Sample Global Feedback}
|
||||
`)[0] as NumericalQuestion;
|
||||
|
||||
const imageMock: TemplateOptions & NumericalQuestion =
|
||||
parse(`
|
||||
::Sample Numerical Title with Image::[markdown]Sample Stem with Image {#=42#Correct!=43#Incorrect! ####Sample Global Feedback with Image}
|
||||
`)[0] as NumericalQuestion;
|
||||
|
||||
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', () => {
|
||||
const { asFragment } = render(<Numerical {...plainTextMock} />);
|
||||
|
|
|
|||
|
|
@ -1,63 +1,35 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import ShortAnswer from 'src/components/GiftTemplate/templates/ShortAnswer';
|
||||
import { TemplateOptions, ShortAnswer as ShortAnswerType } from 'src/components/GiftTemplate/templates/types';
|
||||
import ShortAnswer from 'src/components/GiftTemplate/templates/ShortAnswerTemplate';
|
||||
import { TemplateOptions } from 'src/components/GiftTemplate/templates/types';
|
||||
import { parse, ShortAnswerQuestion } from 'gift-pegjs';
|
||||
|
||||
// Mock the nanoid function
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: jest.fn(() => 'mocked-id')
|
||||
}));
|
||||
|
||||
const plainTextMock: TemplateOptions & ShortAnswerType = {
|
||||
type: 'Short',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Short Answer Title',
|
||||
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 plainTextMock: TemplateOptions & ShortAnswerQuestion =
|
||||
parse(`
|
||||
::Sample Short Answer Title:: Sample Stem {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####Sample Global Feedback}
|
||||
`)[0] as ShortAnswerQuestion;
|
||||
|
||||
const katexMock: TemplateOptions & ShortAnswerType = {
|
||||
type: 'Short',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Short Answer Title',
|
||||
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 katexMock: TemplateOptions & ShortAnswerQuestion =
|
||||
parse(`
|
||||
::Sample Short Answer Title:: $$\\frac\\{zzz\\}\\{yyy\\}$$ {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####[html]Sample Global Feedback}
|
||||
`)[0] as ShortAnswerQuestion;
|
||||
|
||||
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 = {
|
||||
type: 'Short',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Short Answer Title with Image',
|
||||
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 moodleMock: TemplateOptions & ShortAnswerQuestion =
|
||||
parse(`
|
||||
::Sample Short Answer Title:: Sample Stem {=%1%Answer 1#Correct! =%1%Answer 2#Correct!####[moodle]Sample Global Feedback}
|
||||
`)[0] as ShortAnswerQuestion;
|
||||
|
||||
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', () => {
|
||||
const { asFragment } = render(<ShortAnswer {...plainTextMock} />);
|
||||
|
|
|
|||
|
|
@ -2,56 +2,27 @@ import React from 'react';
|
|||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
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
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: jest.fn(() => 'mocked-id')
|
||||
}));
|
||||
|
||||
const plainTextMock: TemplateOptions & TrueFalseType = {
|
||||
type: 'TF',
|
||||
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 plainTextMock: TemplateOptions & TrueFalseQuestion =
|
||||
parse(`::Sample True/False Title::Sample Stem {T#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||
|
||||
const katexMock: TemplateOptions & TrueFalseType = {
|
||||
type: 'TF',
|
||||
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 katexMock: TemplateOptions & TrueFalseQuestion =
|
||||
parse(`::Sample True/False Title::$$\\frac{zzz}{yyy}$$ {T#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||
|
||||
const moodleMock: TemplateOptions & TrueFalseType = {
|
||||
type: 'TF',
|
||||
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 moodleMock: TemplateOptions & TrueFalseQuestion =
|
||||
parse(`::Sample True/False Title::[moodle]Sample Stem{TRUE#Correct!#Incorrect!####Sample Global Feedback}`)[0] as TrueFalseQuestion;
|
||||
|
||||
const imageMock: TemplateOptions & TrueFalseType = {
|
||||
type: 'TF',
|
||||
hasEmbeddedAnswers: false,
|
||||
title: 'Sample Short Answer Title with Image',
|
||||
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" />' }
|
||||
};
|
||||
const imageMock: TemplateOptions & ShortAnswerQuestion =
|
||||
parse(`::Sample Short Answer Title with Image::
|
||||
[markdown]Sample Stem with Image 
|
||||
{=A =B =C####[html]<img src="https\\://via.placeholder.com/150" alt="Sample Image" />}`)[0] as ShortAnswerQuestion;
|
||||
|
||||
test('TrueFalse snapshot test with plain text', () => {
|
||||
const { asFragment } = render(<TrueFalse {...plainTextMock} />);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
|||
</div>
|
||||
<p style="
|
||||
color: hsl(0, 0%, 0%);
|
||||
">Sample Stem with Image
|
||||
">Sample Stem with Image <img src="https://example.com/cat.jpg" alt="">
|
||||
</p>
|
||||
<div>
|
||||
<span style="
|
||||
|
|
@ -59,9 +59,7 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
|||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
width: 90%;
|
||||
" placeholder="Answer 1
|
||||
, Answer 2
|
||||
, <img src="https://via.placeholder.com/150" alt="Sample Image" />">
|
||||
" placeholder="Answer 1, Answer 2">
|
||||
</div>
|
||||
<div style="
|
||||
position: relative;
|
||||
|
|
@ -73,7 +71,8 @@ exports[`ShortAnswer snapshot test with image 1`] = `
|
|||
border-radius: 6px;
|
||||
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>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@ import React from 'react';
|
|||
import { render, fireEvent, screen, act } from '@testing-library/react';
|
||||
import '@testing-library/jest-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', () => {
|
||||
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 = {
|
||||
questionTitle: 'Sample True/False Question',
|
||||
correctAnswer: true,
|
||||
question: trueFalseQuestion,
|
||||
handleOnSubmitAnswer: mockHandleSubmitAnswer,
|
||||
showAnswer: false
|
||||
};
|
||||
|
|
@ -19,7 +21,7 @@ describe('TrueFalseQuestion Component', () => {
|
|||
beforeEach(() => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TrueFalseQuestion questionContent={{ text: sampleStem, format: 'plain' }} {...sampleProps} />
|
||||
<TrueFalseQuestionDisplay {...sampleProps} />
|
||||
</MemoryRouter>);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@ import React from 'react';
|
|||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Question } from 'gift-pegjs';
|
||||
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(
|
||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||
|
||||
::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")
|
||||
question.id = (index + 1).toString();
|
||||
const newMockQuestion = question;
|
||||
return newMockQuestion;
|
||||
return {question : newMockQuestion as BaseQuestion};
|
||||
});
|
||||
|
||||
const mockSubmitAnswer = jest.fn();
|
||||
|
|
|
|||
|
|
@ -1,256 +1,21 @@
|
|||
import Template, { ErrorTemplate } from './templates';
|
||||
import { GIFTQuestion } from './templates/types';
|
||||
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 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 QuestionContainer from './QuestionContainer';
|
||||
import Title from './Title';
|
||||
import { textType } from './TextType';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import { textType } from './TextTypeTemplate';
|
||||
import { ParagraphStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
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 textType from './TextType';
|
||||
import { TemplateOptions } from './types';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import { state } from '.';
|
||||
import { theme } from '../constants';
|
||||
import { TextFormat } from 'gift-pegjs';
|
||||
|
||||
interface GlobalFeedbackOptions extends TemplateOptions {
|
||||
feedback: Question['globalFeedback'];
|
||||
}
|
||||
type GlobalFeedbackOptions = TemplateOptions & TextFormat;
|
||||
|
||||
export default function GlobalFeedback({ feedback }: GlobalFeedbackOptions): string {
|
||||
export default function GlobalFeedbackTemplate({ format, text }: GlobalFeedbackOptions): string {
|
||||
const Container = `
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
|
|
@ -19,9 +18,9 @@ export default function GlobalFeedback({ feedback }: GlobalFeedbackOptions): str
|
|||
box-shadow: 0px 2px 5px ${theme(state.theme, 'gray400', 'black800')};
|
||||
`;
|
||||
|
||||
return feedback !== null
|
||||
return (format && text)
|
||||
? `<div style="${Container}">
|
||||
<p>${textType({ text: feedback })}</p>
|
||||
<p>${textType({format: format, text: text})}</p>
|
||||
</div>`
|
||||
: ``;
|
||||
}
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
import { TemplateOptions, Matching as MatchingType } from './types';
|
||||
import QuestionContainer from './QuestionContainer';
|
||||
import Title from './Title';
|
||||
import textType from './TextType';
|
||||
import GlobalFeedback from './GlobalFeedback';
|
||||
import { TemplateOptions } from './types';
|
||||
import QuestionContainer from './QuestionContainerTemplate';
|
||||
import Title from './TitleTemplate';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import GlobalFeedback from './GlobalFeedbackTemplate';
|
||||
import { ParagraphStyle, SelectStyle } from '../constants';
|
||||
import { state } from '.';
|
||||
import { MatchingQuestion } from 'gift-pegjs';
|
||||
|
||||
type MatchingOptions = TemplateOptions & MatchingType;
|
||||
type MatchingOptions = TemplateOptions & MatchingQuestion;
|
||||
|
||||
interface MatchAnswerOptions extends TemplateOptions {
|
||||
choices: MatchingType['matchPairs'];
|
||||
choices: MatchingQuestion['matchPairs'];
|
||||
}
|
||||
|
||||
export default function Matching({
|
||||
export default function MatchingTemplate({
|
||||
title,
|
||||
stem,
|
||||
formattedStem,
|
||||
matchPairs,
|
||||
globalFeedback
|
||||
formattedGlobalFeedback
|
||||
}: MatchingOptions): string {
|
||||
return `${QuestionContainer({
|
||||
children: [
|
||||
|
|
@ -24,11 +25,9 @@ export default function Matching({
|
|||
type: 'Appariement',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
||||
text: stem
|
||||
})}</p>`,
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
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 result = choices
|
||||
.map(({ subquestion }) => {
|
||||
.map(({ formattedSubquestion }) => {
|
||||
return `
|
||||
<div style="${OptionTable} ${ParagraphStyle(state.theme)}">
|
||||
${textType({ text: subquestion })}
|
||||
${textType(formattedSubquestion)}
|
||||
</div>
|
||||
<div>
|
||||
<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 { TemplateOptions, TextFormat, Choice, MultipleChoice as MultipleChoiceType } from './types';
|
||||
import textType from './TextType';
|
||||
import AnswerIcon from './AnswerIcon';
|
||||
import { TemplateOptions } from './types';
|
||||
import {textType} from './TextTypeTemplate';
|
||||
import AnswerIcon from './AnswerIconTemplate';
|
||||
import { state } from '.';
|
||||
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 {
|
||||
weight: Choice['weight'];
|
||||
correct: Choice['isCorrect'];
|
||||
weight: TextChoice['weight'];
|
||||
correct: TextChoice['isCorrect'];
|
||||
}
|
||||
|
||||
export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerOptions) {
|
||||
export default function MultipleChoiceAnswersTemplate({ choices }: MultipleChoiceAnswerOptions) {
|
||||
const id = `id${nanoid(8)}`;
|
||||
|
||||
const isMultipleAnswer = choices.filter(({ isCorrect }) => isCorrect === true).length === 0;
|
||||
|
|
@ -23,7 +24,7 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
|||
isMultipleAnswer ? ` ou plusieurs` : ``
|
||||
}:</span>`;
|
||||
const result = choices
|
||||
.map(({ weight, isCorrect, text, feedback }) => {
|
||||
.map(({ weight, isCorrect, formattedText, formattedFeedback }) => {
|
||||
const CustomLabel = `
|
||||
display: inline-block;
|
||||
padding: 0.2em 0 0.2em 0;
|
||||
|
|
@ -31,7 +32,7 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
|||
|
||||
const inputId = `id${nanoid(6)}`;
|
||||
|
||||
const isPositiveWeight = weight !== null && weight > 0;
|
||||
const isPositiveWeight = (weight != undefined) && (weight > 0);
|
||||
const isCorrectOption = isMultipleAnswer ? isPositiveWeight : isCorrect;
|
||||
|
||||
return `
|
||||
|
|
@ -41,10 +42,10 @@ export default function MultipleChoiceAnswers({ choices }: MultipleChoiceAnswerO
|
|||
}" id="${inputId}" name="${id}">
|
||||
${AnswerWeight({ correct: isCorrectOption, weight: weight })}
|
||||
<label style="${CustomLabel} ${ParagraphStyle(state.theme)}" for="${inputId}">
|
||||
${textType({ text: text as TextFormat })}
|
||||
${textType(formattedText)}
|
||||
</label>
|
||||
${AnswerIcon({ correct: isCorrectOption })}
|
||||
${AnswerFeedback({ feedback: feedback })}
|
||||
${AnswerFeedback({ formattedFeedback: formattedFeedback })}
|
||||
</input>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -80,10 +81,10 @@ function AnswerWeight({ weight, correct }: AnswerWeightOptions): string {
|
|||
: ``;
|
||||
}
|
||||
|
||||
function AnswerFeedback({ feedback }: AnswerFeedbackOptions): string {
|
||||
function AnswerFeedback({ formattedFeedback }: AnswerFeedbackOptions): string {
|
||||
const Container = `
|
||||
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 QuestionContainer from './QuestionContainer';
|
||||
import Title from './Title';
|
||||
import textType from './TextType';
|
||||
import GlobalFeedback from './GlobalFeedback';
|
||||
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 './index';
|
||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||
|
||||
type ShortAnswerOptions = TemplateOptions & ShortAnswerType;
|
||||
type AnswerOptions = TemplateOptions & Pick<ShortAnswerType, 'choices'>;
|
||||
type ShortAnswerOptions = TemplateOptions & ShortAnswerQuestion;
|
||||
type AnswerOptions = TemplateOptions & Pick<ShortAnswerQuestion, 'choices'>;
|
||||
|
||||
export default function ShortAnswer({
|
||||
export default function ShortAnswerTemplate({
|
||||
title,
|
||||
stem,
|
||||
formattedStem,
|
||||
choices,
|
||||
globalFeedback
|
||||
formattedGlobalFeedback
|
||||
}: ShortAnswerOptions): string {
|
||||
return `${QuestionContainer({
|
||||
children: [
|
||||
|
|
@ -21,18 +22,16 @@ export default function ShortAnswer({
|
|||
type: 'Réponse courte',
|
||||
title: title
|
||||
}),
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType({
|
||||
text: stem
|
||||
})}</p>`,
|
||||
`<p style="${ParagraphStyle(state.theme)}">${textType(formattedStem)}</p>`,
|
||||
Answers({ choices: choices }),
|
||||
GlobalFeedback({ feedback: globalFeedback })
|
||||
formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
|
||||
]
|
||||
})}`;
|
||||
}
|
||||
|
||||
function Answers({ choices }: AnswerOptions): string {
|
||||
const placeholder = choices
|
||||
.map(({ text }) => textType({ text: text as TextFormat }))
|
||||
.map(({ text }) => textType({ format: '', text: text }))
|
||||
.join(', ');
|
||||
return `
|
||||
<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
|
||||
let parsedText = '';
|
||||
switch (formattedText.format) {
|
||||
case '':
|
||||
case 'moodle':
|
||||
case 'plain':
|
||||
// Replace newlines with <br> tags
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { TemplateOptions, Question } from './types';
|
||||
import { TemplateOptions } from './types';
|
||||
import { state } from '.';
|
||||
import { theme } from '../constants';
|
||||
import { Question } from 'gift-pegjs';
|
||||
|
||||
// Type is string to allow for custom question type text (e,g, "Multiple Choice")
|
||||
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 {
|
||||
GIFTQuestion,
|
||||
ParsedGIFTQuestion as GIFTQuestion,
|
||||
// Category as CategoryType,
|
||||
// Description as DescriptionType,
|
||||
MultipleChoice as MultipleChoiceType,
|
||||
Numerical as NumericalType,
|
||||
ShortAnswer as ShortAnswerType,
|
||||
MultipleChoiceQuestion as MultipleChoiceType,
|
||||
NumericalQuestion as NumericalType,
|
||||
ShortAnswerQuestion as ShortAnswerType,
|
||||
// Essay as EssayType,
|
||||
TrueFalse as TrueFalseType,
|
||||
Matching as MatchingType,
|
||||
DisplayOptions
|
||||
} from './types';
|
||||
TrueFalseQuestion as TrueFalseType,
|
||||
// MatchingQuestion as MatchingType,
|
||||
} from 'gift-pegjs';
|
||||
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' };
|
||||
|
||||
|
|
@ -37,21 +38,21 @@ export default function Template(
|
|||
// ...(keys as DescriptionType)
|
||||
// });
|
||||
case 'MC':
|
||||
return MultipleChoice({
|
||||
return MultipleChoiceTemplate({
|
||||
...(keys as MultipleChoiceType)
|
||||
});
|
||||
case 'Numerical':
|
||||
return Numerical({ ...(keys as NumericalType) });
|
||||
return NumericalTemplate({ ...(keys as NumericalType) });
|
||||
case 'Short':
|
||||
return ShortAnswer({
|
||||
return ShortAnswerTemplate({
|
||||
...(keys as ShortAnswerType)
|
||||
});
|
||||
// case 'Essay':
|
||||
// return Essay({ ...(keys as EssayType) });
|
||||
case 'TF':
|
||||
return TrueFalse({ ...(keys as TrueFalseType) });
|
||||
case 'Matching':
|
||||
return Matching({ ...(keys as MatchingType) });
|
||||
return TrueFalseTemplate({ ...(keys as TrueFalseType) });
|
||||
// case 'Matching':
|
||||
// return Matching({ ...(keys as MatchingType) });
|
||||
default:
|
||||
// TODO: throw error for unsupported question types?
|
||||
// throw new Error(`Unsupported question type: ${type}`);
|
||||
|
|
@ -66,13 +67,13 @@ export function ErrorTemplate(text: string, options?: Partial<DisplayOptions>):
|
|||
}
|
||||
|
||||
export {
|
||||
Category,
|
||||
CategoryTemplate,
|
||||
DescriptionTemplate as Description,
|
||||
Essay,
|
||||
Matching,
|
||||
MultipleChoice,
|
||||
Numerical,
|
||||
ShortAnswer,
|
||||
TrueFalse,
|
||||
EssayTemplate as Essay,
|
||||
MatchingTemplate as Matching,
|
||||
MultipleChoiceTemplate as MultipleChoice,
|
||||
NumericalTemplate as Numerical,
|
||||
ShortAnswerTemplate as ShortAnswer,
|
||||
TrueFalseTemplate as TrueFalse,
|
||||
Error
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
TableRow
|
||||
} from '@mui/material';
|
||||
import { StudentType } from '../../Types/StudentType';
|
||||
import { formatLatex } from '../GiftTemplate/templates/TextType';
|
||||
import { formatLatex } from '../GiftTemplate/templates/TextTypeTemplate';
|
||||
|
||||
interface LiveResultsProps {
|
||||
socket: Socket | null;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
||||
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { MultipleChoiceQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-container">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({text: question.formattedStem})) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
</div>
|
||||
<div className="choices-wrapper mb-1">
|
||||
{question.choices.map((choice, i) => {
|
||||
|
|
@ -48,13 +48,13 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
(choice.isCorrect ? '✅' : '❌')}
|
||||
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||
<div className={`answer-text ${selected}`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: choice.formattedText })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedText)) }} />
|
||||
</div>
|
||||
</Button>
|
||||
{choice.formattedFeedback && showAnswer && (
|
||||
<div className="feedback-container mb-1 mt-1/2">
|
||||
{choice.isCorrect ? '✅' : '❌'}
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: choice.formattedFeedback })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(choice.formattedFeedback)) }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -63,7 +63,7 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
{question.formattedGlobalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">
|
||||
<p>${textType({ text: question.formattedGlobalFeedback })}</p>
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
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 { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer, isMultipleNumericalAnswer } from 'gift-pegjs/typeGuards';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
|
@ -41,13 +41,13 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-wrapper">
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedStem })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
<div className="correct-answer-text mb-2">{correctAnswer}</div>
|
||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedGlobalFeedback })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
|
|
@ -65,7 +65,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
{question.formattedGlobalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: question.formattedGlobalFeedback })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedGlobalFeedback)) }} />
|
||||
</div>
|
||||
)}
|
||||
{handleOnSubmitAnswer && (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// Question;tsx
|
||||
import React from 'react';
|
||||
import { Question } from 'gift-pegjs';
|
||||
|
||||
import TrueFalseQuestion from './TrueFalseQuestion/TrueFalseQuestion';
|
||||
import TrueFalseQuestionDisplay from './TrueFalseQuestionDisplay/TrueFalseQuestionDisplay';
|
||||
import MultipleChoiceQuestionDisplay from './MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay';
|
||||
import NumericalQuestionDisplay from './NumericalQuestionDisplay/NumericalQuestionDisplay';
|
||||
import ShortAnswerQuestionDisplay from './ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
||||
|
|
@ -27,12 +26,10 @@ const QuestionDisplay: React.FC<QuestionProps> = ({
|
|||
switch (question?.type) {
|
||||
case 'TF':
|
||||
questionTypeComponent = (
|
||||
<TrueFalseQuestion
|
||||
questionContent={question.formattedStem}
|
||||
correctAnswer={question.isTrue}
|
||||
<TrueFalseQuestionDisplay
|
||||
question={question}
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
showAnswer={showAnswer}
|
||||
globalFeedback={question.formattedGlobalFeedback?.text}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
||||
import { textType } from '../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { ShortAnswerQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
return (
|
||||
<div className="question-wrapper">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({text: question.formattedStem})) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
</div>
|
||||
{showAnswer ? (
|
||||
<>
|
||||
|
|
@ -30,7 +30,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
|||
))}
|
||||
</div>
|
||||
{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>}
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -2,33 +2,32 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import '../questionStyle.css';
|
||||
import { Button } from '@mui/material';
|
||||
import { textType } from '../../GiftTemplate/templates/TextType';
|
||||
import { TextFormat } from 'gift-pegjs';
|
||||
import { TrueFalseQuestion } from 'gift-pegjs';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { textType } from 'src/components/GiftTemplate/templates/TextTypeTemplate';
|
||||
|
||||
interface Props {
|
||||
questionContent: TextFormat;
|
||||
correctAnswer: boolean;
|
||||
globalFeedback?: string | undefined;
|
||||
question: TrueFalseQuestion;
|
||||
handleOnSubmitAnswer?: (answer: boolean) => void;
|
||||
showAnswer?: boolean;
|
||||
}
|
||||
|
||||
const TrueFalseQuestion: React.FC<Props> = (props) => {
|
||||
const { questionContent, correctAnswer, showAnswer, handleOnSubmitAnswer, globalFeedback } =
|
||||
const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||
const { question, showAnswer, handleOnSubmitAnswer } =
|
||||
props;
|
||||
const [answer, setAnswer] = useState<boolean | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setAnswer(undefined);
|
||||
}, [questionContent]);
|
||||
}, [question]);
|
||||
|
||||
const selectedTrue = answer ? 'selected' : '';
|
||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||
const correctAnswer = question.isTrue === answer;
|
||||
return (
|
||||
<div className="question-container">
|
||||
<div className="question content">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType({ text: questionContent })) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.formattedStem)) }} />
|
||||
</div>
|
||||
<div className="choices-wrapper mb-1">
|
||||
<Button
|
||||
|
|
@ -50,8 +49,22 @@ const TrueFalseQuestion: React.FC<Props> = (props) => {
|
|||
<div className={`answer-text ${selectedFalse}`}>Faux</div>
|
||||
</Button>
|
||||
</div>
|
||||
{globalFeedback && showAnswer && (
|
||||
<div className="global-feedback mb-2">{globalFeedback}</div>
|
||||
{/* selected TRUE, show True feedback if it exists */}
|
||||
{showAnswer && answer && question.trueFormattedFeedback && (
|
||||
<div className="true-feedback mb-2">
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(textType(question.trueFormattedFeedback)) }} />
|
||||
</div>
|
||||
)}
|
||||
{/* 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 && (
|
||||
<Button
|
||||
|
|
@ -68,4 +81,4 @@ const TrueFalseQuestion: React.FC<Props> = (props) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default TrueFalseQuestion;
|
||||
export default TrueFalseQuestionDisplay;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// StudentModeQuiz.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import QuestionComponent from '../Questions/QuestionDisplay';
|
||||
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
// import { QuestionService } from '../../services/QuestionService';
|
||||
|
|
@ -8,6 +8,7 @@ import { Button } from '@mui/material';
|
|||
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
||||
//import { ChevronLeft, ChevronRight } from '@mui/icons-material';
|
||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Question } from 'gift-pegjs';
|
||||
|
||||
interface StudentModeQuizProps {
|
||||
questions: QuestionType[];
|
||||
|
|
@ -63,7 +64,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
</div>
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question}
|
||||
question={questionInfos.question as Question}
|
||||
showAnswer={isAnswerSubmitted}
|
||||
/>
|
||||
<div className="center-h-align mt-1/2">
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
// TeacherModeQuiz.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import QuestionComponent from '../Questions/QuestionDisplay';
|
||||
import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||
|
||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
// import { QuestionService } from '../../services/QuestionService';
|
||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
||||
import { Question } from 'gift-pegjs';
|
||||
|
||||
interface TeacherModeQuizProps {
|
||||
questionInfos: QuestionType;
|
||||
|
|
@ -63,7 +64,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
) : (
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question}
|
||||
question={questionInfos.question as Question}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
{feedbackMessage}
|
||||
<QuestionComponent
|
||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question}
|
||||
question={questionInfos.question as Question}
|
||||
showAnswer={true}
|
||||
/>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { GIFTQuestion, parse } from 'gift-pegjs';
|
||||
import { QuestionType } from '../../../Types/QuestionType';
|
||||
import { ParsedGIFTQuestion, BaseQuestion, parse, Question } from 'gift-pegjs';
|
||||
import { isSimpleNumericalAnswer, isRangeNumericalAnswer, isHighLowNumericalAnswer } from "gift-pegjs/typeGuards";
|
||||
import LiveResultsComponent from 'src/components/LiveResults/LiveResults';
|
||||
// import { QuestionService } from '../../../services/QuestionService';
|
||||
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 DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
//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 { QuestionType } from 'src/Types/QuestionType';
|
||||
|
||||
const ManageRoom: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -277,7 +278,7 @@ const ManageRoom: React.FC = () => {
|
|||
const parsedQuestions = [] as QuestionType[];
|
||||
|
||||
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();
|
||||
});
|
||||
if (parsedQuestions.length === 0) return null;
|
||||
|
|
@ -347,7 +348,7 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
const answerText = answer.toString();
|
||||
if (questionInfo) {
|
||||
const question = questionInfo.question as GIFTQuestion;
|
||||
const question = questionInfo.question as ParsedGIFTQuestion;
|
||||
if (question.type === 'TF') {
|
||||
return (
|
||||
(question.isTrue && answerText == 'true') ||
|
||||
|
|
@ -355,33 +356,23 @@ const ManageRoom: React.FC = () => {
|
|||
);
|
||||
} else if (question.type === 'MC') {
|
||||
return question.choices.some(
|
||||
(choice) => choice.isCorrect && choice.text.text === answerText
|
||||
(choice) => choice.isCorrect && choice.formattedText.text === answerText
|
||||
);
|
||||
} else if (question.type === 'Numerical') {
|
||||
if (question.choices && !Array.isArray(question.choices)) {
|
||||
if (
|
||||
question.choices.type === 'high-low' &&
|
||||
question.choices.numberHigh &&
|
||||
question.choices.numberLow
|
||||
) {
|
||||
if (isHighLowNumericalAnswer(question.choices[0])) {
|
||||
const choice = question.choices[0];
|
||||
const answerNumber = parseFloat(answerText);
|
||||
if (!isNaN(answerNumber)) {
|
||||
return (
|
||||
answerNumber <= question.choices.numberHigh &&
|
||||
answerNumber >= question.choices.numberLow
|
||||
answerNumber <= choice.numberHigh &&
|
||||
answerNumber >= choice.numberLow
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (question.choices && Array.isArray(question.choices)) {
|
||||
if (
|
||||
question.choices[0].text.type === 'range' &&
|
||||
question.choices[0].text.number &&
|
||||
question.choices[0].text.range
|
||||
) {
|
||||
if (isRangeNumericalAnswer(question.choices[0])) {
|
||||
const answerNumber = parseFloat(answerText);
|
||||
const range = question.choices[0].text.range;
|
||||
const correctAnswer = question.choices[0].text.number;
|
||||
const range = question.choices[0].range;
|
||||
const correctAnswer = question.choices[0].number;
|
||||
if (!isNaN(answerNumber)) {
|
||||
return (
|
||||
answerNumber <= correctAnswer + range &&
|
||||
|
|
@ -389,19 +380,15 @@ const ManageRoom: React.FC = () => {
|
|||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
question.choices[0].text.type === 'simple' &&
|
||||
question.choices[0].text.number
|
||||
) {
|
||||
if (isSimpleNumericalAnswer(question.choices[0])) {
|
||||
const answerNumber = parseFloat(answerText);
|
||||
if (!isNaN(answerNumber)) {
|
||||
return answerNumber === question.choices[0].text.number;
|
||||
}
|
||||
return answerNumber === question.choices[0].number;
|
||||
}
|
||||
}
|
||||
} else if (question.type === 'Short') {
|
||||
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 && (
|
||||
<QuestionDisplay
|
||||
showAnswer={false}
|
||||
question={currentQuestion?.question}
|
||||
question={currentQuestion?.question as Question}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,19 @@
|
|||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"src/*": ["src/*"]
|
||||
"src/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
|
|
@ -17,7 +22,6 @@
|
|||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
|
|
@ -25,6 +29,16 @@
|
|||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
// "exclude": [
|
||||
// // "src/components/GiftTemplate/**/*",
|
||||
// // "src/__tests__/components/GiftTemplate/**/*",
|
||||
// ],
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue