Skip to content

Commit

Permalink
Support add questions
Browse files Browse the repository at this point in the history
  • Loading branch information
leung018 committed Nov 13, 2023
1 parent 64fd9c6 commit cb561dc
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 69 deletions.
123 changes: 123 additions & 0 deletions src/app/components/mc/editor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,127 @@ describe('QuestionSetEditor', () => {
}),
)
})

it('should save question set after multiple clicks of add choice and add question', () => {
const editorRepo = QuestionSetRepoFactory.createTestInstance()
const { getAllByText, getByLabelText, getByText } = render(
QuestionSetEditorUIService.createTestInstance({
editorRepo,
}).getElement(),
)
fireEvent.change(getByLabelText('Question Set Name:'), {
target: { value: 'Test name' },
})

fireEvent.change(getByLabelText('Question 1:'), {
target: { value: '1 + 1 = ?' },
})
fireEvent.change(getByLabelText('answer of question 1 choice 1'), {
target: { value: '2' },
})
fireEvent.change(getByLabelText('answer of question 1 choice 2'), {
target: { value: '0' },
})
fireEvent.click(getByLabelText('question 1 choice 1 is correct answer'))

fireEvent.click(getByText('Add Question'))

fireEvent.change(getByLabelText('Question 2:'), {
target: { value: '1 + 2 = ?' },
})
fireEvent.change(getByLabelText('answer of question 2 choice 1'), {
target: { value: '3' },
})
fireEvent.change(getByLabelText('answer of question 2 choice 2'), {
target: { value: '0' },
})
fireEvent.click(getByLabelText('question 2 choice 1 is correct answer'))

fireEvent.click(getByText('Add Question'))

fireEvent.change(getByLabelText('Question 3:'), {
target: { value: '1 + 3 = ?' },
})
fireEvent.change(getByLabelText('answer of question 3 choice 1'), {
target: { value: '1' },
})
fireEvent.change(getByLabelText('answer of question 3 choice 2'), {
target: { value: '3' },
})
const question3AddChoice = getAllByText(
'Add Choice',
).pop() as HTMLButtonElement
fireEvent.click(question3AddChoice)
fireEvent.click(question3AddChoice)
fireEvent.change(getByLabelText('answer of question 3 choice 3'), {
target: { value: '0' },
})
fireEvent.change(getByLabelText('answer of question 3 choice 4'), {
target: { value: 'None of the above' },
})
fireEvent.click(getByLabelText('question 3 choice 4 is correct answer'))
fireEvent.click(getByLabelText('question 3 choice 4 is fixed position'))

fireEvent.click(getByText('Save'))

const actualQuestionSet = editorRepo.getQuestionSetByName('Test name')
expect(actualQuestionSet.questions).toEqual([
{
title: '1 + 1 = ?',
mc: new MultipleChoice({
choices: [
{
answer: '2',
isFixedPosition: false,
},
{
answer: '0',
isFixedPosition: false,
},
],
correctChoiceIndex: 0,
}),
},
{
title: '1 + 2 = ?',
mc: new MultipleChoice({
choices: [
{
answer: '3',
isFixedPosition: false,
},
{
answer: '0',
isFixedPosition: false,
},
],
correctChoiceIndex: 0,
}),
},
{
title: '1 + 3 = ?',
mc: new MultipleChoice({
choices: [
{
answer: '1',
isFixedPosition: false,
},
{
answer: '3',
isFixedPosition: false,
},
{
answer: '0',
isFixedPosition: false,
},
{
answer: 'None of the above',
isFixedPosition: true,
},
],
correctChoiceIndex: 3,
}),
},
])
})
})
173 changes: 104 additions & 69 deletions src/app/components/mc/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
QuestionSetRepoFactory,
} from '../../../repo/question_set'
import { QuestionSet } from '../../../model/question_set'
import { MultipleChoiceBuilder } from '../../../model/mc'
import { MultipleChoice, MultipleChoiceBuilder } from '../../../model/mc'

export class QuestionSetEditorUIService {
static create() {
Expand Down Expand Up @@ -85,30 +85,51 @@ function QuestionSetEditor({
return choiceInputs
}

const mapFormDataToQuestionSet = (formData: FormData): QuestionSet => {
const mapFormDataToQuestionSet = (
formData: FormData,
numOfQuestions: number,
): QuestionSet => {
const questions: {
title: string
mc: MultipleChoice
}[] = []
for (
let questionIndex = 0;
questionIndex < numOfQuestions;
questionIndex++
) {
const numOfChoices = questionIndexToNumOfChoices.get(
questionIndex,
) as number
const mcBuilder = new MultipleChoiceBuilder()
for (let choiceIndex = 0; choiceIndex < numOfChoices; choiceIndex++) {
mcBuilder.appendChoice({
answer: formData.get(
`question-${questionIndex}-choice-${choiceIndex}-answer`,
) as string,
isFixedPosition:
formData.get(
`question-${questionIndex}-choice-${choiceIndex}-is-fixed-position`,
) === 'on',
})
if (
formData.get(
`question-${questionIndex}-choice-${choiceIndex}-is-correct`,
) === 'on'
) {
mcBuilder.setCorrectChoiceIndex(choiceIndex)
}
}

questions.push({
title: formData.get(`question-${questionIndex}-title`) as string,
mc: mcBuilder.build(),
})
}

return {
name: formData.get('question-set-name') as string,
questions: [
{
title: formData.get('question-0-title') as string,
mc: (() => {
const numOfChoices = questionIndexToNumOfChoices.get(0) as number
const mcBuilder = new MultipleChoiceBuilder()
for (let i = 0; i < numOfChoices; i++) {
mcBuilder.appendChoice({
answer: formData.get(`question-0-choice-${i}-answer`) as string,
isFixedPosition:
formData.get(`question-0-choice-${i}-is-fixed-position`) ===
'on',
})
if (formData.get(`question-0-choice-${i}-is-correct`) === 'on') {
mcBuilder.setCorrectChoiceIndex(i)
}
}
return mcBuilder.build()
})(),
},
],
questions,
}
}

Expand All @@ -126,54 +147,63 @@ function QuestionSetEditor({
</label>
</div>

<div className="mb-8">
<label>
<h2 className="text-lg font-bold mb-2">Question 1:</h2>
<input
type="text"
className="border border-gray-300 px-2 py-1 w-full"
name="question-0-title"
/>
</label>
<div className="form-group">
<h3 className="text-lg font-bold mb-2">Choices:</h3>
<table className="border-collapse border border-slate-400">
<thead>
<tr>
<th className="border border-slate-300">Answer</th>
<th className="border border-slate-300">Correct</th>
<th className="border border-slate-300">Fixed Position</th>
</tr>
</thead>
<tbody>
{renderChoiceInputs(
0,
questionIndexToNumOfChoices.get(0) as number,
)}
</tbody>
</table>

<button
type="button"
className="bg-blue-500 text-white px-4 py-2 rounded"
onClick={() => {
const newMap = new Map<number, number>(
questionIndexToNumOfChoices,
)
newMap.set(
0,
(questionIndexToNumOfChoices.get(0) as number) + 1,
)
setQuestionIndexToNumOfChoices(newMap)
}}
>
Add Choice
</button>
</div>
</div>
{Array.from(questionIndexToNumOfChoices).map(
([questionIndex, numOfChoices]) => {
return (
<div key={questionIndex} className="mb-8">
<label>
<h2 className="text-lg font-bold mb-2">
Question {questionIndex + 1}:
</h2>
<input
type="text"
className="border border-gray-300 px-2 py-1 w-full"
name={`question-${questionIndex}-title`}
/>
</label>
<div className="form-group">
<h3 className="text-lg font-bold mb-2">Choices:</h3>
<table className="border-collapse border border-slate-400">
<thead>
<tr>
<th className="border border-slate-300">Answer</th>
<th className="border border-slate-300">Correct</th>
<th className="border border-slate-300">
Fixed Position
</th>
</tr>
</thead>
<tbody>
{renderChoiceInputs(questionIndex, numOfChoices)}
</tbody>
</table>

<button
type="button"
className="bg-blue-500 text-white px-4 py-2 rounded"
onClick={() => {
const newMap = new Map<number, number>(
questionIndexToNumOfChoices,
)
newMap.set(questionIndex, numOfChoices + 1)
setQuestionIndexToNumOfChoices(newMap)
}}
>
Add Choice
</button>
</div>
</div>
)
},
)}
<button
type="button"
className="bg-blue-500 text-white px-4 py-2 rounded"
onClick={() => {
const newMap = new Map<number, number>(questionIndexToNumOfChoices)
newMap.set(newMap.size, 2)
setQuestionIndexToNumOfChoices(newMap)
}}
>
Add Question
</button>
Expand All @@ -183,7 +213,12 @@ function QuestionSetEditor({
type="button"
className="bg-green-500 text-white px-4 py-2 rounded"
onClick={() =>
onSave(mapFormDataToQuestionSet(new FormData(document.forms[0])))
onSave(
mapFormDataToQuestionSet(
new FormData(document.forms[0]),
questionIndexToNumOfChoices.size,
),
)
}
>
Save
Expand Down

0 comments on commit cb561dc

Please sign in to comment.