mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge 0f52440946 into 112062c0b2
This commit is contained in:
commit
c65fea8399
10 changed files with 363 additions and 114 deletions
|
|
@ -1,44 +1,21 @@
|
||||||
import React from 'react';
|
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { NumericalQuestion, parse, ParsedGIFTQuestion } from 'gift-pegjs';
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { parse, NumericalQuestion } from 'gift-pegjs';
|
||||||
|
import React from 'react';
|
||||||
import NumericalQuestionDisplay from 'src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay';
|
import NumericalQuestionDisplay from 'src/components/QuestionsDisplay/NumericalQuestionDisplay/NumericalQuestionDisplay';
|
||||||
|
|
||||||
const questions = parse(
|
describe('NumericalQuestionDisplay Component', () => {
|
||||||
`
|
|
||||||
::Sample Question 1:: Question stem
|
|
||||||
{
|
|
||||||
#5..10
|
|
||||||
}`
|
|
||||||
) as ParsedGIFTQuestion[];
|
|
||||||
|
|
||||||
const question = questions[0] as NumericalQuestion;
|
|
||||||
|
|
||||||
describe('NumericalQuestion parse', () => {
|
|
||||||
const q = questions[0];
|
|
||||||
|
|
||||||
it('The question is Numerical', () => {
|
|
||||||
expect(q.type).toBe('Numerical');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('NumericalQuestion Component', () => {
|
|
||||||
const mockHandleOnSubmitAnswer = jest.fn();
|
const mockHandleOnSubmitAnswer = jest.fn();
|
||||||
|
const question =
|
||||||
|
parse('::Sample Numerical Question:: What is 2+2? {#4}')[0] as NumericalQuestion;
|
||||||
|
|
||||||
const sampleProps = {
|
const sampleProps = {
|
||||||
question: question,
|
|
||||||
handleOnSubmitAnswer: mockHandleOnSubmitAnswer,
|
handleOnSubmitAnswer: mockHandleOnSubmitAnswer,
|
||||||
showAnswer: false
|
showAnswer: false
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
render(
|
render(<NumericalQuestionDisplay question={question} {...sampleProps} />);
|
||||||
<MemoryRouter>
|
|
||||||
<NumericalQuestionDisplay
|
|
||||||
{...sampleProps}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
|
|
@ -55,13 +32,13 @@ describe('NumericalQuestion Component', () => {
|
||||||
expect(inputElement.value).toBe('7');
|
expect(inputElement.value).toBe('7');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Submit button should be disable if nothing is entered', () => {
|
it('Submit button should be disabled if nothing is entered', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
expect(submitButton).toBeDisabled();
|
expect(submitButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('not submited answer if nothing is entered', () => {
|
it('does not submit answer if nothing is entered', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
|
|
@ -79,4 +56,52 @@ describe('NumericalQuestion Component', () => {
|
||||||
|
|
||||||
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith(7);
|
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith(7);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it('renders correctly with the correct answer shown', () => {
|
||||||
|
render(<NumericalQuestionDisplay question={question} {...sampleProps} showAnswer={true} passedAnswer={4} />);
|
||||||
|
expect(screen.getByText('Réponse(s) accepté(es):')).toBeInTheDocument();
|
||||||
|
expect(screen.getAllByText('4')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles input change and checks if the answer is correct', () => {
|
||||||
|
const inputElement = screen.getByTestId('number-input') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: '4' } });
|
||||||
|
|
||||||
|
expect(inputElement.value).toBe('4');
|
||||||
|
|
||||||
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits the correct answer', () => {
|
||||||
|
const inputElement = screen.getByTestId('number-input') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: '4' } });
|
||||||
|
|
||||||
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an incorrect answer', () => {
|
||||||
|
const inputElement = screen.getByTestId('number-input') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: '5' } });
|
||||||
|
|
||||||
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays feedback when the answer is shown', () => {
|
||||||
|
render(<NumericalQuestionDisplay question={question} {...sampleProps} showAnswer={true} passedAnswer={5} />);
|
||||||
|
expect(screen.getByText('❌ Incorrect!')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Réponse(s) accepté(es):')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('5')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
|
||||||
import { render, screen, fireEvent, within } from '@testing-library/react';
|
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import { parse, ShortAnswerQuestion } from 'gift-pegjs';
|
import { parse, ShortAnswerQuestion } from 'gift-pegjs';
|
||||||
|
import React from 'react';
|
||||||
import ShortAnswerQuestionDisplay from 'src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
import ShortAnswerQuestionDisplay from 'src/components/QuestionsDisplay/ShortAnswerQuestionDisplay/ShortAnswerQuestionDisplay';
|
||||||
|
|
||||||
describe('ShortAnswerQuestion Component', () => {
|
describe('ShortAnswerQuestion Component', () => {
|
||||||
|
|
@ -20,28 +20,26 @@ describe('ShortAnswerQuestion Component', () => {
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
expect(screen.getByText(question.formattedStem.text)).toBeInTheDocument();
|
expect(screen.getByText(question.formattedStem.text)).toBeInTheDocument();
|
||||||
const container = screen.getByLabelText('short-answer-input');
|
const inputElement = screen.getByRole('textbox') as HTMLInputElement;
|
||||||
const inputElement = within(container).getByRole('textbox') as HTMLInputElement;
|
|
||||||
expect(inputElement).toBeInTheDocument();
|
expect(inputElement).toBeInTheDocument();
|
||||||
expect(screen.getByText('Répondre')).toBeInTheDocument();
|
expect(screen.getByText('Répondre')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles input change correctly', () => {
|
it('handles input change correctly', () => {
|
||||||
const container = screen.getByLabelText('short-answer-input');
|
const inputElement = screen.getByRole('textbox') as HTMLInputElement;
|
||||||
const inputElement = within(container).getByRole('textbox') as HTMLInputElement;
|
|
||||||
|
|
||||||
fireEvent.change(inputElement, { target: { value: 'User Input' } });
|
fireEvent.change(inputElement, { target: { value: 'User Input' } });
|
||||||
|
|
||||||
expect(inputElement.value).toBe('User Input');
|
expect(inputElement.value).toBe('User Input');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Submit button should be disable if nothing is entered', () => {
|
it('Submit button should be disabled if nothing is entered', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
expect(submitButton).toBeDisabled();
|
expect(submitButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('not submitted answer if nothing is entered', () => {
|
it('does not submit answer if nothing is entered', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
|
|
@ -49,17 +47,51 @@ describe('ShortAnswerQuestion Component', () => {
|
||||||
expect(mockHandleSubmitAnswer).not.toHaveBeenCalled();
|
expect(mockHandleSubmitAnswer).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('submits answer correctly', () => {
|
it('renders correctly with the correct answer shown', () => {
|
||||||
const container = screen.getByLabelText('short-answer-input');
|
render(<ShortAnswerQuestionDisplay question={question} {...sampleProps} showAnswer={true} passedAnswer="Correct Answer" />);
|
||||||
const inputElement = within(container).getByRole('textbox') as HTMLInputElement;
|
expect(screen.getByText('Réponse(s) accepté(es):')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Correct Answer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles input change and checks if the answer is correct', () => {
|
||||||
|
const inputElement = screen.getByRole('textbox') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: 'Correct Answer' } });
|
||||||
|
|
||||||
|
expect(inputElement.value).toBe('Correct Answer');
|
||||||
|
|
||||||
// const inputElement = screen.getByRole('textbox', { name: 'short-answer-input'}) as HTMLInputElement;
|
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
fireEvent.change(inputElement, { target: { value: 'User Input' } });
|
|
||||||
|
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith('User Input');
|
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith('Correct Answer');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it('submits the correct answer', () => {
|
||||||
|
const inputElement = screen.getByRole('textbox') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: 'Correct Answer' } });
|
||||||
|
|
||||||
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith('Correct Answer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an incorrect answer', () => {
|
||||||
|
const inputElement = screen.getByRole('textbox') as HTMLInputElement;
|
||||||
|
|
||||||
|
fireEvent.change(inputElement, { target: { value: 'Incorrect Answer' } });
|
||||||
|
|
||||||
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith('Incorrect Answer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays feedback when the answer is shown', () => {
|
||||||
|
render(<ShortAnswerQuestionDisplay question={question} {...sampleProps} showAnswer={true} passedAnswer="Incorrect Answer" />);
|
||||||
|
expect(screen.getByText('❌ Incorrect!')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Réponse(s) accepté(es):')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Incorrect Answer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -66,6 +66,18 @@ describe('TeacherModeQuiz', () => {
|
||||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
|
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('handles shows feedback for answered question', () => {
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Option B'));
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Répondre'));
|
||||||
|
});
|
||||||
|
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option B', 1)
|
||||||
|
expect(screen.getByText('❌ Incorrect!')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test('handles shows feedback for an already answered question', () => {
|
test('handles shows feedback for an already answered question', () => {
|
||||||
// Answer the first question
|
// Answer the first question
|
||||||
act(() => {
|
act(() => {
|
||||||
|
|
@ -106,7 +118,7 @@ describe('TeacherModeQuiz', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if the feedback dialog is shown again
|
// Check if the feedback dialog is shown again
|
||||||
expect(screen.getByText('Rétroaction')).toBeInTheDocument();
|
expect(screen.getByText('❌ Incorrect!')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles disconnect button click', () => {
|
test('handles disconnect button click', () => {
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,29 @@ interface Props {
|
||||||
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
let disableButton = false;
|
let disableButton = false;
|
||||||
if(handleOnSubmitAnswer === undefined){
|
if (handleOnSubmitAnswer === undefined) {
|
||||||
disableButton = true;
|
disableButton = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (passedAnswer !== undefined) {
|
if (passedAnswer !== undefined) {
|
||||||
setAnswer(passedAnswer);
|
setAnswer(passedAnswer);
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
const isCorrect = question.choices.some((choice) => choice.isCorrect && choice.formattedText.text === answer as string);
|
||||||
|
setisGoodAnswer(isCorrect);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnClickAnswer = (choice: string) => {
|
const handleOnClickAnswer = (choice: string) => {
|
||||||
setAnswer(choice);
|
setAnswer(choice);
|
||||||
};
|
};
|
||||||
|
|
@ -36,12 +46,24 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
const alphabet = alpha.map((x) => String.fromCharCode(x));
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="question-container">
|
|
||||||
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
|
|
||||||
{question.choices.map((choice, i) => {
|
{question.choices.map((choice, i) => {
|
||||||
const selected = answer === choice.formattedText.text ? 'selected' : '';
|
const selected = answer === choice.formattedText.text ? 'selected' : '';
|
||||||
return (
|
return (
|
||||||
|
|
@ -51,17 +73,17 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
className="button-wrapper"
|
className="button-wrapper"
|
||||||
disabled={disableButton}
|
disabled={disableButton}
|
||||||
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
onClick={() => !showAnswer && handleOnClickAnswer(choice.formattedText.text)}>
|
||||||
{showAnswer? (<div> {(choice.isCorrect ? '✅' : '❌')}</div>)
|
{showAnswer ? (<div> {(choice.isCorrect ? '✅' : '❌')}</div>)
|
||||||
:``}
|
: ``}
|
||||||
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
<div className={`circle ${selected}`}>{alphabet[i]}</div>
|
||||||
<div className={`answer-text ${selected}`}>
|
<div className={`answer-text ${selected}`}>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedText) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedText) }} />
|
||||||
</div>
|
</div>
|
||||||
{choice.formattedFeedback && showAnswer && (
|
{choice.formattedFeedback && showAnswer && (
|
||||||
<div className="feedback-container mb-1 mt-1/2">
|
<div className="feedback-container mb-1 mt-1/2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(choice.formattedFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -70,22 +92,24 @@ const MultipleChoiceQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && showAnswer && (
|
{question.formattedGlobalFeedback && showAnswer && (
|
||||||
<div className="global-feedback mb-2">
|
<div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!showAnswer && handleOnSubmitAnswer && (
|
{!showAnswer && handleOnSubmitAnswer && (
|
||||||
|
<div className="submit-button-container">
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
className='submit-button'
|
||||||
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
answer !== "" && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
answer !== "" && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
||||||
}
|
}
|
||||||
disabled={answer === '' || answer === null}
|
disabled={answer === '' || answer === null}
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,13 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } =
|
||||||
props;
|
props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
|
const [isMultpleAnswer, setIsMultpleAnswer] = useState<boolean>(false);
|
||||||
|
|
||||||
const correctAnswers = question.choices;
|
const correctAnswers = question.choices;
|
||||||
|
const correctAnswersList: number[] = [];
|
||||||
|
const correctAnswersPhrases: string[] = [];
|
||||||
|
|
||||||
let correctAnswer = '';
|
let correctAnswer = '';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -27,6 +33,34 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
if(isMultpleAnswer) {
|
||||||
|
correctAnswers.forEach((answers) => {
|
||||||
|
if(isSimpleNumericalAnswer(answers) && answer === answers.number) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
} else if(isRangeNumericalAnswer(answers) && answer as number >= answers.number - answers.range && answer as number <= answers.number + answers.range) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
} else if(isHighLowNumericalAnswer(answers) && answer as number >= answers.numberLow && answer as number <= answers.numberHigh) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
if(isSimpleNumericalAnswer(answers) && answer === answers.number) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
} else if(isRangeNumericalAnswer(answers) && answer as number >= answers.number - answers.range && answer as number <= answers.number + answers.range) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
} else if(isHighLowNumericalAnswer(answers) && answer as number >= answers.numberLow && answer as number <= answers.numberHigh) {
|
||||||
|
setisGoodAnswer(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
//const isSingleAnswer = correctAnswers.length === 1;
|
//const isSingleAnswer = correctAnswers.length === 1;
|
||||||
|
|
||||||
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
if (isSimpleNumericalAnswer(correctAnswers[0])) {
|
||||||
|
|
@ -38,28 +72,57 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const choice = correctAnswers[0] as HighLowNumericalAnswer;
|
const choice = correctAnswers[0] as HighLowNumericalAnswer;
|
||||||
correctAnswer = `Entre ${choice.numberLow} et ${choice.numberHigh}`;
|
correctAnswer = `Entre ${choice.numberLow} et ${choice.numberHigh}`;
|
||||||
} else if (isMultipleNumericalAnswer(correctAnswers[0])) {
|
} else if (isMultipleNumericalAnswer(correctAnswers[0])) {
|
||||||
correctAnswer = `MultipleNumericalAnswer is not supported yet`;
|
setIsMultpleAnswer(true);
|
||||||
} else {
|
correctAnswers.forEach((answers) => {
|
||||||
|
if(isSimpleNumericalAnswer(answers)) {
|
||||||
|
correctAnswersPhrases.push(`${(answers as SimpleNumericalAnswer).number}`);
|
||||||
|
} else if(isRangeNumericalAnswer(answers)) {
|
||||||
|
correctAnswersPhrases.push(`Entre ${answers.number - answers.range} et ${answers.number + answers.range}`);
|
||||||
|
} else if(isHighLowNumericalAnswer(answers)) {
|
||||||
|
correctAnswersPhrases.push(`Entre ${answers.numberLow} et ${answers.numberHigh}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
throw new Error('Unknown numerical answer type');
|
throw new Error('Unknown numerical answer type');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
<div className="correct-answer-text mb-2">
|
<div className="correct-answer-text mb-1">
|
||||||
<strong>La bonne réponse est: </strong>
|
<div>
|
||||||
{correctAnswer}</div>
|
<div className="question-title">
|
||||||
<span>
|
Réponse(s) accepté(es):
|
||||||
<strong>Votre réponse est: </strong>{answer.toString()}
|
</div>
|
||||||
</span>
|
<div className="accepted-answers">
|
||||||
|
{correctAnswer}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="question-title">
|
||||||
|
Votre réponse est: </div>
|
||||||
|
<div className="accepted-answers">{answer}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -80,7 +143,9 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{handleOnSubmitAnswer && (
|
{handleOnSubmitAnswer && (
|
||||||
|
<div className="submit-button-container">
|
||||||
<Button
|
<Button
|
||||||
|
className='submit-button'
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
answer !== undefined &&
|
answer !== undefined &&
|
||||||
|
|
@ -91,6 +156,7 @@ const NumericalQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -17,34 +17,59 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
const { question, showAnswer, handleOnSubmitAnswer, passedAnswer } = props;
|
||||||
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
const [answer, setAnswer] = useState<AnswerType>(passedAnswer || '');
|
||||||
|
const [isGoodAnswer, setisGoodAnswer] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (passedAnswer !== undefined) {
|
if (passedAnswer !== undefined) {
|
||||||
setAnswer(passedAnswer);
|
setAnswer(passedAnswer);
|
||||||
}
|
}
|
||||||
}, [passedAnswer]);
|
}, [passedAnswer]);
|
||||||
console.log("Answer" , answer);
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAnswer();
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
const isCorrect = question.choices.some((choice) => String(choice.text).toLowerCase() === String(answer).toLowerCase());
|
||||||
|
setisGoodAnswer(isCorrect);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="question-wrapper">
|
<div className="question-wrapper">
|
||||||
<div className="question content">
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{isGoodAnswer ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
{showAnswer ? (
|
{showAnswer ? (
|
||||||
<>
|
<>
|
||||||
<div className="correct-answer-text mb-1">
|
<div className="correct-answer-text mb-1">
|
||||||
<span>
|
<div>
|
||||||
<strong>La bonne réponse est: </strong>
|
<div className="question-title">
|
||||||
|
Réponse(s) accepté(es):
|
||||||
{question.choices.map((choice) => (
|
|
||||||
<div key={choice.text} className="mb-1">
|
|
||||||
{choice.text}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
{question.choices.map((choice) => (
|
||||||
</span>
|
<div key={choice.text} className="accepted-answers">
|
||||||
<span>
|
{choice.text}
|
||||||
<strong>Votre réponse est: </strong>{answer}
|
</div>
|
||||||
</span>
|
))}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="question-title">
|
||||||
|
Votre réponse est: </div>
|
||||||
|
<div className="accepted-answers">{answer}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
{question.formattedGlobalFeedback && <div className="global-feedback mb-2">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedGlobalFeedback) }} />
|
||||||
|
|
@ -65,7 +90,9 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{handleOnSubmitAnswer && (
|
{handleOnSubmitAnswer && (
|
||||||
|
<div className="submit-button-container">
|
||||||
<Button
|
<Button
|
||||||
|
className='submit-button'
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
answer !== undefined &&
|
answer !== undefined &&
|
||||||
|
|
@ -76,6 +103,7 @@ const ShortAnswerQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,19 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
const selectedTrue = answer ? 'selected' : '';
|
const selectedTrue = answer ? 'selected' : '';
|
||||||
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
const selectedFalse = answer !== undefined && !answer ? 'selected' : '';
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-wrapper">
|
||||||
|
{showAnswer && (
|
||||||
|
<div>
|
||||||
|
<div className='question-feedback-validation'>
|
||||||
|
{answer === question.isTrue ? '✅ Correct! ' : '❌ Incorrect!'}
|
||||||
|
</div>
|
||||||
|
<div className="question-title">
|
||||||
|
Question :
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate(question.formattedStem) }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -92,7 +104,9 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!showAnswer && handleOnSubmitAnswer && (
|
{!showAnswer && handleOnSubmitAnswer && (
|
||||||
|
<div className="submit-button-container">
|
||||||
<Button
|
<Button
|
||||||
|
className='submit-button'
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
answer !== undefined && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
answer !== undefined && handleOnSubmitAnswer && handleOnSubmitAnswer(answer)
|
||||||
|
|
@ -102,6 +116,7 @@ const TrueFalseQuestionDisplay: React.FC<Props> = (props) => {
|
||||||
>
|
>
|
||||||
Répondre
|
Répondre
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,8 +21,6 @@
|
||||||
.question-wrapper {
|
.question-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.question-wrapper .katex {
|
.question-wrapper .katex {
|
||||||
|
|
@ -31,7 +28,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.katex * {
|
.katex * {
|
||||||
font-family: 'KaTeX_Main' /* to display characters like \neq properly */
|
font-family: 'KaTeX_Main'
|
||||||
|
/* to display characters like \neq properly */
|
||||||
}
|
}
|
||||||
|
|
||||||
.circle {
|
.circle {
|
||||||
|
|
@ -97,15 +95,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-positive-weight {
|
.answer-positive-weight {
|
||||||
background-color: hsl(
|
background-color: hsl(120, 100%, 90%);
|
||||||
120, 100%, 90%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-zero-or-less-weight {
|
.answer-zero-or-less-weight {
|
||||||
background-color: hsl(
|
background-color: hsl(0, 100%, 90%);
|
||||||
0, 100%, 90%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-text.selected {
|
.answer-text.selected {
|
||||||
|
|
@ -119,7 +113,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback-container {
|
.feedback-container {
|
||||||
display: inline-block !important; /* override the parent */
|
display: inline-block !important;
|
||||||
|
/* override the parent */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: 1.1rem;
|
margin-left: 1.1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -136,12 +131,12 @@
|
||||||
/* height: 1em; */
|
/* height: 1em; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.global-feedback {
|
.global-feedback {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
background-color: hsl(43, 100%, 94%);
|
background-color: hsl(43, 100%, 94%);
|
||||||
color: hsl(43, 95%, 9%);
|
color: hsl(43, 95%, 9%);
|
||||||
|
margin-top: 2rem;
|
||||||
border: hsl(36, 84%, 93%) 1px solid;
|
border: hsl(36, 84%, 93%) 1px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
||||||
|
|
@ -156,6 +151,7 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
box-shadow: 0px 2px 5px hsl(0, 0%, 74%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.false-feedback {
|
.false-feedback {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
|
|
@ -169,3 +165,57 @@
|
||||||
.choices-wrapper {
|
.choices-wrapper {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.correct-answer-text {
|
||||||
|
text-align: left;
|
||||||
|
/* Align text to the left */
|
||||||
|
width: 100%;
|
||||||
|
/* Ensure it takes the full width of its container */
|
||||||
|
}
|
||||||
|
|
||||||
|
.accepted-answers {
|
||||||
|
display: inline-block;
|
||||||
|
/* Display elements side by side */
|
||||||
|
border-radius: 6px;
|
||||||
|
/* Add rounded corners */
|
||||||
|
padding: 0.5rem;
|
||||||
|
/* Add padding for spacing */
|
||||||
|
margin: 0.5rem;
|
||||||
|
/* Add margin for spacing between elements */
|
||||||
|
background-color: hsl(0, 0%, 87%);
|
||||||
|
/* Optional: Add a background color for emphasis */
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-feedback-validation {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
/* Increase the font size */
|
||||||
|
font-weight: bold;
|
||||||
|
/* Make the text bold */
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-title {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-content {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
width: min-content;
|
||||||
|
margin-left: 19rem;}
|
||||||
|
|
||||||
|
.submit-button-container{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
@ -60,7 +60,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||||
<div >
|
<div >
|
||||||
<b>Question {questionInfos.question.id}/{questions.length}</b>
|
<b>Question {questionInfos.question.id}/{questions.length}</b>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-auto">
|
<div className="overflow-auto" style={{ width: '50%', alignItems: 'center', justifyContent: 'center', margin: 'auto' }}>
|
||||||
<div className="question-component-container">
|
<div className="question-component-container">
|
||||||
<div className="mb-5">
|
<div className="mb-5">
|
||||||
{/* <QuestionNavigation
|
{/* <QuestionNavigation
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import QuestionComponent from '../QuestionsDisplay/QuestionDisplay';
|
||||||
import '../../pages/Student/JoinRoom/joinRoom.css';
|
import '../../pages/Student/JoinRoom/joinRoom.css';
|
||||||
import { QuestionType } from '../../Types/QuestionType';
|
import { QuestionType } from '../../Types/QuestionType';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
import { Dialog, DialogContent, DialogActions, Button } from '@mui/material';
|
||||||
import { Question } from 'gift-pegjs';
|
import { Question } from 'gift-pegjs';
|
||||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||||
|
|
@ -94,7 +94,6 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
open={isFeedbackDialogOpen}
|
open={isFeedbackDialogOpen}
|
||||||
onClose={handleFeedbackDialogClose}
|
onClose={handleFeedbackDialogClose}
|
||||||
>
|
>
|
||||||
<DialogTitle>Rétroaction</DialogTitle>
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div style={{
|
<div style={{
|
||||||
wordWrap: 'break-word',
|
wordWrap: 'break-word',
|
||||||
|
|
@ -102,8 +101,6 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
maxHeight: '400px',
|
maxHeight: '400px',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px' }}
|
|
||||||
>Question : </div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue