diff --git a/client/src/Types/QuestionType.tsx b/client/src/Types/QuestionType.tsx
new file mode 100644
index 0000000..94f8cc6
--- /dev/null
+++ b/client/src/Types/QuestionType.tsx
@@ -0,0 +1,5 @@
+import { BaseQuestion } from "gift-pegjs";
+
+export interface QuestionType {
+ question: BaseQuestion;
+}
diff --git a/client/src/__tests__/components/GiftTemplate/TextType.test.ts b/client/src/__tests__/components/GiftTemplate/TextType.test.ts
index 7a9993f..109852e 100644
--- a/client/src/__tests__/components/GiftTemplate/TextType.test.ts
+++ b/client/src/__tests__/components/GiftTemplate/TextType.test.ts
@@ -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,
world!
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 = 'E=mc2';
- 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 = 'a+b=c ? E=mc2';
- 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.\\begin{pmatrix}
a&b \\\\
c&d
\\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 = 'Bold\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 = 'yes';
- 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
diff --git a/client/src/__tests__/components/GiftTemplate/templates/AnswerIcon.test.tsx b/client/src/__tests__/components/GiftTemplate/templates/AnswerIcon.test.tsx
index 0a9226b..efaed61 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/AnswerIcon.test.tsx
+++ b/client/src/__tests__/components/GiftTemplate/templates/AnswerIcon.test.tsx
@@ -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', () => {
diff --git a/client/src/__tests__/components/GiftTemplate/templates/MultipleChoice.test.tsx b/client/src/__tests__/components/GiftTemplate/templates/MultipleChoice.test.tsx
index 53c86c0..bdecb06 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/MultipleChoice.test.tsx
+++ b/client/src/__tests__/components/GiftTemplate/templates/MultipleChoice.test.tsx
@@ -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: '
' }, 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: '
' }, 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: '
' }, 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: '
' }, 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: '
' },
+ formattedStem: { format: 'markdown', text: '
' },
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: '
' }, 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: '
' }, 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', () => {
@@ -130,4 +131,4 @@ test('MultipleChoice snapshot test with image using markdown text format', () =>
test('MultipleChoice snapshot test with 2 images using markdown text format', () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
-});
\ No newline at end of file
+});
diff --git a/client/src/__tests__/components/GiftTemplate/templates/Numerical.test.tsx b/client/src/__tests__/components/GiftTemplate/templates/Numerical.test.tsx
index e28f80f..77a3277 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/Numerical.test.tsx
+++ b/client/src/__tests__/components/GiftTemplate/templates/Numerical.test.tsx
@@ -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: '
' } }
- ],
- globalFeedback: { format: 'plain', text: 'Sample Global Feedback with Image' }
-};
test('Numerical snapshot test with plain text', () => {
const { asFragment } = render();
@@ -76,4 +54,4 @@ test('Numerical snapshot test with moodle', () => {
test('Numerical snapshot test with image', () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
-});
\ No newline at end of file
+});
diff --git a/client/src/__tests__/components/GiftTemplate/templates/ShortAnswer.test.tsx b/client/src/__tests__/components/GiftTemplate/templates/ShortAnswer.test.tsx
index 9928099..34c444b 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/ShortAnswer.test.tsx
+++ b/client/src/__tests__/components/GiftTemplate/templates/ShortAnswer.test.tsx
@@ -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: '
' }, 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();
@@ -77,4 +49,4 @@ test('ShortAnswer snapshot test with moodle', () => {
test('ShortAnswer snapshot test with image', () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
-});
\ No newline at end of file
+});
diff --git a/client/src/__tests__/components/GiftTemplate/templates/TrueFalse.test.tsx b/client/src/__tests__/components/GiftTemplate/templates/TrueFalse.test.tsx
index bbb717e..8804c4d 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/TrueFalse.test.tsx
+++ b/client/src/__tests__/components/GiftTemplate/templates/TrueFalse.test.tsx
@@ -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: '
' }
-};
+const imageMock: TemplateOptions & ShortAnswerQuestion =
+ parse(`::Sample Short Answer Title with Image::
+ [markdown]Sample Stem with Image 
+ {=A =B =C####[html]
}`)[0] as ShortAnswerQuestion;
test('TrueFalse snapshot test with plain text', () => {
const { asFragment } = render();
@@ -71,4 +42,4 @@ test('TrueFalse snapshot test with moodle', () => {
test('TrueFalse snapshot test with image', () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
-});
\ No newline at end of file
+});
diff --git a/client/src/__tests__/components/GiftTemplate/templates/__snapshots__/ShortAnswer.test.tsx.snap b/client/src/__tests__/components/GiftTemplate/templates/__snapshots__/ShortAnswer.test.tsx.snap
index 6204be4..f70b8d5 100644
--- a/client/src/__tests__/components/GiftTemplate/templates/__snapshots__/ShortAnswer.test.tsx.snap
+++ b/client/src/__tests__/components/GiftTemplate/templates/__snapshots__/ShortAnswer.test.tsx.snap
@@ -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>
`;
diff --git a/client/src/__tests__/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.test.tsx b/client/src/__tests__/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.test.tsx
index 9f60d6c..04620a1 100644
--- a/client/src/__tests__/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.test.tsx
+++ b/client/src/__tests__/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.test.tsx
@@ -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(
-
+
);
});
diff --git a/client/src/__tests__/pages/Student/StudentModeQuiz/StudentModeQuiz.test.tsx b/client/src/__tests__/pages/Student/StudentModeQuiz/StudentModeQuiz.test.tsx
index f031983..801cdd5 100644
--- a/client/src/__tests__/pages/Student/StudentModeQuiz/StudentModeQuiz.test.tsx
+++ b/client/src/__tests__/pages/Student/StudentModeQuiz/StudentModeQuiz.test.tsx
@@ -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();
diff --git a/client/src/components/GiftTemplate/index.ts b/client/src/components/GiftTemplate/index.ts
index 1f39302..04de882 100644
--- a/client/src/components/GiftTemplate/index.ts
+++ b/client/src/components/GiftTemplate/index.ts
@@ -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');
diff --git a/client/src/components/GiftTemplate/templates/AnswerIcon.ts b/client/src/components/GiftTemplate/templates/AnswerIconTemplate.ts
similarity index 100%
rename from client/src/components/GiftTemplate/templates/AnswerIcon.ts
rename to client/src/components/GiftTemplate/templates/AnswerIconTemplate.ts
diff --git a/client/src/components/GiftTemplate/templates/Category.ts b/client/src/components/GiftTemplate/templates/Category.ts
deleted file mode 100644
index c279b29..0000000
--- a/client/src/components/GiftTemplate/templates/Category.ts
+++ /dev/null
@@ -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
- })
- })}`;
-}
diff --git a/client/src/components/GiftTemplate/templates/CategoryTemplate.ts b/client/src/components/GiftTemplate/templates/CategoryTemplate.ts
new file mode 100644
index 0000000..ef15ad9
--- /dev/null
+++ b/client/src/components/GiftTemplate/templates/CategoryTemplate.ts
@@ -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
+ })
+ })}`;
+}
diff --git a/client/src/components/GiftTemplate/templates/DescriptionTemplate.ts b/client/src/components/GiftTemplate/templates/DescriptionTemplate.ts
index 52c18ed..fa4b70d 100644
--- a/client/src/components/GiftTemplate/templates/DescriptionTemplate.ts
+++ b/client/src/components/GiftTemplate/templates/DescriptionTemplate.ts
@@ -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';
diff --git a/client/src/components/GiftTemplate/templates/Error.ts b/client/src/components/GiftTemplate/templates/ErrorTemplate.ts
similarity index 100%
rename from client/src/components/GiftTemplate/templates/Error.ts
rename to client/src/components/GiftTemplate/templates/ErrorTemplate.ts
diff --git a/client/src/components/GiftTemplate/templates/Essay.ts b/client/src/components/GiftTemplate/templates/Essay.ts
deleted file mode 100644
index a003d78..0000000
--- a/client/src/components/GiftTemplate/templates/Essay.ts
+++ /dev/null
@@ -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
- }),
- `
${textType({
- text: stem
- })}
`,
- ``,
- GlobalFeedback({ feedback: globalFeedback })
- ]
- })}`;
-}
diff --git a/client/src/components/GiftTemplate/templates/EssayTemplate.ts b/client/src/components/GiftTemplate/templates/EssayTemplate.ts
new file mode 100644
index 0000000..5697846
--- /dev/null
+++ b/client/src/components/GiftTemplate/templates/EssayTemplate.ts
@@ -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
+ }),
+ `${textType(formattedStem)}
`,
+ ``,
+ (formattedGlobalFeedback && formattedGlobalFeedback.text) ?
+ GlobalFeedbackTemplate(formattedGlobalFeedback) : ``
+ ]
+ })}`;
+}
diff --git a/client/src/components/GiftTemplate/templates/GlobalFeedback.ts b/client/src/components/GiftTemplate/templates/GlobalFeedbackTemplate.ts
similarity index 59%
rename from client/src/components/GiftTemplate/templates/GlobalFeedback.ts
rename to client/src/components/GiftTemplate/templates/GlobalFeedbackTemplate.ts
index 6826c7c..2c5b6d4 100644
--- a/client/src/components/GiftTemplate/templates/GlobalFeedback.ts
+++ b/client/src/components/GiftTemplate/templates/GlobalFeedbackTemplate.ts
@@ -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)
? `
-
${textType({ text: feedback })}
+
${textType({format: format, text: text})}
`
: ``;
}
diff --git a/client/src/components/GiftTemplate/templates/Matching.ts b/client/src/components/GiftTemplate/templates/MatchingTemplate.ts
similarity index 77%
rename from client/src/components/GiftTemplate/templates/Matching.ts
rename to client/src/components/GiftTemplate/templates/MatchingTemplate.ts
index 97e4dce..d36b8ad 100644
--- a/client/src/components/GiftTemplate/templates/Matching.ts
+++ b/client/src/components/GiftTemplate/templates/MatchingTemplate.ts
@@ -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
}),
- `${textType({
- text: stem
- })}
`,
+ `${textType(formattedStem)}
`,
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 `
- ${textType({ text: subquestion })}
+ ${textType(formattedSubquestion)}
`;
@@ -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 ? `${textType({ text: feedback })}` : ``;
+ return formattedFeedback ? `${textType(formattedFeedback)}` : ``;
}
diff --git a/client/src/components/GiftTemplate/templates/MultipleChoiceTemplate.ts b/client/src/components/GiftTemplate/templates/MultipleChoiceTemplate.ts
new file mode 100644
index 0000000..aef9ec1
--- /dev/null
+++ b/client/src/components/GiftTemplate/templates/MultipleChoiceTemplate.ts
@@ -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
+ }),
+ `${textType(formattedStem)}
`,
+ MultipleChoiceAnswers({ choices: choices }),
+ formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ''
+ ]
+ })}`;
+}
diff --git a/client/src/components/GiftTemplate/templates/Numerical.ts b/client/src/components/GiftTemplate/templates/Numerical.ts
deleted file mode 100644
index 08567a2..0000000
--- a/client/src/components/GiftTemplate/templates/Numerical.ts
+++ /dev/null
@@ -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;
-
-export default function Numerical({
- title,
- stem,
- choices,
- globalFeedback
-}: NumericalOptions): string {
- return `${QuestionContainer({
- children: [
- Title({
- type: 'Numérique',
- title: title
- }),
- `${textType({
- text: stem
- })}
`,
- 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 `
-
- Réponse:
-
- `;
-}
-
-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 ``;
- }
-}
diff --git a/client/src/components/GiftTemplate/templates/NumericalTemplate.ts b/client/src/components/GiftTemplate/templates/NumericalTemplate.ts
new file mode 100644
index 0000000..1af6372
--- /dev/null
+++ b/client/src/components/GiftTemplate/templates/NumericalTemplate.ts
@@ -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;
+
+export default function NumericalTemplate({
+ title,
+ formattedStem,
+ choices,
+ formattedGlobalFeedback
+}: NumericalOptions): string {
+ return `${QuestionContainer({
+ children: [
+ Title({
+ type: 'Numérique',
+ title: title
+ }),
+ `${textType(formattedStem)}
`,
+ 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 `
+
+ Réponse:
+
+ `;
+}
+
+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 ``;
+ }
+}
diff --git a/client/src/components/GiftTemplate/templates/QuestionContainer.ts b/client/src/components/GiftTemplate/templates/QuestionContainerTemplate.ts
similarity index 100%
rename from client/src/components/GiftTemplate/templates/QuestionContainer.ts
rename to client/src/components/GiftTemplate/templates/QuestionContainerTemplate.ts
diff --git a/client/src/components/GiftTemplate/templates/ShortAnswer.ts b/client/src/components/GiftTemplate/templates/ShortAnswerTemplate.ts
similarity index 53%
rename from client/src/components/GiftTemplate/templates/ShortAnswer.ts
rename to client/src/components/GiftTemplate/templates/ShortAnswerTemplate.ts
index a46282d..f29cea3 100644
--- a/client/src/components/GiftTemplate/templates/ShortAnswer.ts
+++ b/client/src/components/GiftTemplate/templates/ShortAnswerTemplate.ts
@@ -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;
+type ShortAnswerOptions = TemplateOptions & ShortAnswerQuestion;
+type AnswerOptions = TemplateOptions & Pick;
-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
}),
- `${textType({
- text: stem
- })}
`,
+ `${textType(formattedStem)}
`,
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 `
diff --git a/client/src/components/GiftTemplate/templates/TextType.ts b/client/src/components/GiftTemplate/templates/TextTypeTemplate.ts
similarity index 99%
rename from client/src/components/GiftTemplate/templates/TextType.ts
rename to client/src/components/GiftTemplate/templates/TextTypeTemplate.ts
index 799981f..0777324 100644
--- a/client/src/components/GiftTemplate/templates/TextType.ts
+++ b/client/src/components/GiftTemplate/templates/TextTypeTemplate.ts
@@ -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
tags
diff --git a/client/src/components/GiftTemplate/templates/Title.ts b/client/src/components/GiftTemplate/templates/TitleTemplate.ts
similarity index 94%
rename from client/src/components/GiftTemplate/templates/Title.ts
rename to client/src/components/GiftTemplate/templates/TitleTemplate.ts
index 8e89553..da9f4ed 100644
--- a/client/src/components/GiftTemplate/templates/Title.ts
+++ b/client/src/components/GiftTemplate/templates/TitleTemplate.ts
@@ -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 {
diff --git a/client/src/components/GiftTemplate/templates/TrueFalse.ts b/client/src/components/GiftTemplate/templates/TrueFalse.ts
deleted file mode 100644
index ff9df73..0000000
--- a/client/src/components/GiftTemplate/templates/TrueFalse.ts
+++ /dev/null
@@ -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
- }),
- `
${textType({
- text: stem
- })}
`,
- MultipleChoiceAnswers({ choices: choices }),
- GlobalFeedback({ feedback: globalFeedback })
- ]
- })}`;
-}
diff --git a/client/src/components/GiftTemplate/templates/TrueFalseTemplate.ts b/client/src/components/GiftTemplate/templates/TrueFalseTemplate.ts
new file mode 100644
index 0000000..4effb4f
--- /dev/null
+++ b/client/src/components/GiftTemplate/templates/TrueFalseTemplate.ts
@@ -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
+ }),
+ `
`,
+ MultipleChoiceAnswersTemplate({ choices: choices }),
+ formattedGlobalFeedback ? GlobalFeedback(formattedGlobalFeedback) : ``
+ ]
+ })}`;
+}
diff --git a/client/src/components/GiftTemplate/templates/index.ts b/client/src/components/GiftTemplate/templates/index.ts
index 555b99d..0ed65e0 100644
--- a/client/src/components/GiftTemplate/templates/index.ts
+++ b/client/src/components/GiftTemplate/templates/index.ts
@@ -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
):
}
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
};
diff --git a/client/src/components/LiveResults/LiveResults.tsx b/client/src/components/LiveResults/LiveResults.tsx
index b7d1c72..6bcd7ce 100644
--- a/client/src/components/LiveResults/LiveResults.tsx
+++ b/client/src/components/LiveResults/LiveResults.tsx
@@ -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;
diff --git a/client/src/components/QuestionsDisplay/MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay.tsx b/client/src/components/QuestionsDisplay/MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay.tsx
index a2eac59..519c110 100644
--- a/client/src/components/QuestionsDisplay/MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay.tsx
+++ b/client/src/components/QuestionsDisplay/MultipleChoiceQuestionDisplay/MultipleChoiceQuestionDisplay.tsx
@@ -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) => {
return (
{question.choices.map((choice, i) => {
@@ -48,13 +48,13 @@ const MultipleChoiceQuestionDisplay: React.FC
= (props) => {
(choice.isCorrect ? '✅' : '❌')}
{alphabet[i]}
{choice.formattedFeedback && showAnswer && (
{choice.isCorrect ? '✅' : '❌'}
-
+
)}
@@ -63,8 +63,8 @@ const MultipleChoiceQuestionDisplay: React.FC
= (props) => {
{question.formattedGlobalFeedback && showAnswer && (
-
${textType({ text: question.formattedGlobalFeedback })}
-
+
+
)}
{!showAnswer && handleOnSubmitAnswer && (
diff --git a/client/src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay.tsx b/client/src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay.tsx
index acf0869..b1e8154 100644
--- a/client/src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay.tsx
+++ b/client/src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay.tsx
@@ -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) => {
return (
{showAnswer ? (
<>
{correctAnswer}
{question.formattedGlobalFeedback &&
}
>
) : (
@@ -65,7 +65,7 @@ const NumericalQuestionDisplay: React.FC
= (props) => {
{question.formattedGlobalFeedback && showAnswer && (
)}
{handleOnSubmitAnswer && (
diff --git a/client/src/components/QuestionsDisplay/QuestionDisplay.tsx b/client/src/components/QuestionsDisplay/QuestionDisplay.tsx
index 934092f..8dfa1b3 100644
--- a/client/src/components/QuestionsDisplay/QuestionDisplay.tsx
+++ b/client/src/components/QuestionsDisplay/QuestionDisplay.tsx
@@ -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 = ({
switch (question?.type) {
case 'TF':
questionTypeComponent = (
-
);
break;
diff --git a/client/src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay.tsx b/client/src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay.tsx
index 5fd729d..55cbba4 100644
--- a/client/src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay.tsx
+++ b/client/src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay.tsx
@@ -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) => {
return (
{showAnswer ? (
<>
@@ -30,7 +30,7 @@ const ShortAnswerQuestionDisplay: React.FC
= (props) => {
))}
{question.formattedGlobalFeedback && }
>
) : (
diff --git a/client/src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.tsx b/client/src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.tsx
index dd5b3e8..1c4fcdb 100644
--- a/client/src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.tsx
+++ b/client/src/components/QuestionsDisplay/TrueFalseQuestionDisplay/TrueFalseQuestionDisplay.tsx
@@ -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) => {
- const { questionContent, correctAnswer, showAnswer, handleOnSubmitAnswer, globalFeedback } =
+const TrueFalseQuestionDisplay: React.FC = (props) => {
+ const { question, showAnswer, handleOnSubmitAnswer } =
props;
const [answer, setAnswer] = useState(undefined);
useEffect(() => {
setAnswer(undefined);
- }, [questionContent]);
+ }, [question]);
const selectedTrue = answer ? 'selected' : '';
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
+ const correctAnswer = question.isTrue === answer;
return (
- {globalFeedback && showAnswer && (
-
{globalFeedback}
+ {/* selected TRUE, show True feedback if it exists */}
+ {showAnswer && answer && question.trueFormattedFeedback && (
+
+ )}
+ {/* selected FALSE, show False feedback if it exists */}
+ {showAnswer && !answer && question.falseFormattedFeedback && (
+
+ )}
+ {question.formattedGlobalFeedback && showAnswer && (
+
)}
{!showAnswer && handleOnSubmitAnswer && (
diff --git a/client/src/components/TeacherModeQuiz/TeacherModeQuiz.tsx b/client/src/components/TeacherModeQuiz/TeacherModeQuiz.tsx
index 850c9ef..ae2d382 100644
--- a/client/src/components/TeacherModeQuiz/TeacherModeQuiz.tsx
+++ b/client/src/components/TeacherModeQuiz/TeacherModeQuiz.tsx
@@ -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 = ({
) : (
)}
@@ -76,7 +77,7 @@ const TeacherModeQuiz: React.FC = ({
{feedbackMessage}
diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
index 207143c..ae9bdfa 100644
--- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
+++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
@@ -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,53 +356,39 @@ 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
- ) {
- const answerNumber = parseFloat(answerText);
- if (!isNaN(answerNumber)) {
- return (
- answerNumber <= question.choices.numberHigh &&
- answerNumber >= question.choices.numberLow
- );
- }
+ if (isHighLowNumericalAnswer(question.choices[0])) {
+ const choice = question.choices[0];
+ const answerNumber = parseFloat(answerText);
+ if (!isNaN(answerNumber)) {
+ return (
+ 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
- ) {
- const answerNumber = parseFloat(answerText);
- const range = question.choices[0].text.range;
- const correctAnswer = question.choices[0].text.number;
- if (!isNaN(answerNumber)) {
- return (
- answerNumber <= correctAnswer + range &&
- answerNumber >= correctAnswer - range
- );
- }
+ if (isRangeNumericalAnswer(question.choices[0])) {
+ const answerNumber = parseFloat(answerText);
+ const range = question.choices[0].range;
+ const correctAnswer = question.choices[0].number;
+ if (!isNaN(answerNumber)) {
+ return (
+ answerNumber <= correctAnswer + range &&
+ answerNumber >= correctAnswer - range
+ );
}
- if (
- question.choices[0].text.type === 'simple' &&
- question.choices[0].text.number
- ) {
- const answerNumber = parseFloat(answerText);
- if (!isNaN(answerNumber)) {
- return answerNumber === question.choices[0].text.number;
- }
+ }
+ if (isSimpleNumericalAnswer(question.choices[0])) {
+ const answerNumber = parseFloat(answerText);
+ if (!isNaN(answerNumber)) {
+ 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 && (
)}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index a6b7592..5115a44 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -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"
+ }
+ ]
}