Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/quiz-question/answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ export const Answer = <AnswerT extends number | string>({
const getRadioWrapperCls = () => {
const cls = [...radioWrapperDefaultClasses];

if (checked && validation?.state === "correct")
if (validation?.state === "correct")
cls.push("border-l-background-success");
if (checked && validation?.state === "incorrect")
if (validation?.state === "incorrect")
cls.push("border-l-background-danger");

return cls.join(" ");
Expand Down
2 changes: 1 addition & 1 deletion src/quiz-question/quiz-question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const QuizQuestion = <AnswerT extends number | string>({
feedback={checked && validation && feedback}
checked={checked}
disabled={disabled}
validation={checked ? validation : undefined}
validation={validation}
/>
);
})}
Expand Down
224 changes: 224 additions & 0 deletions src/quiz/quiz.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,111 @@ const QuizWithValidationAndAnswerFeedback = () => {
);
};

const QuizWithCorrectAnswersShownOnSuccess = () => {
const initialQuestions: Question<number>[] = [
{
question: "Lorem ipsum dolor sit amet",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>`}
getCodeBlockAriaLabel={(codeName) => `${codeName} code example`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 1,
},
{
question: "Consectetur adipiscing elit",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>`}
getCodeBlockAriaLabel={(codeName) => `${codeName} code example`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 2,
},
{
question: "Fugit itaque delectus voluptatem alias aliquid",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>`}
getCodeBlockAriaLabel={(codeName) => `${codeName} code example`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 3,
},
];

const { questions, validateAnswers, correctAnswerCount } = useQuiz({
initialQuestions,
validationMessages: {
correct: "Correct.",
incorrect: "Incorrect.",
},
passingPercent: 50,
showCorrectAnswersOnSuccess: true,
});
const [disabled, setDisabled] = useState(false);

const handleSubmit = () => {
validateAnswers();
setDisabled(true);
};

return (
<div>
<div aria-live="polite">
{!!correctAnswerCount && (
<p className="text-foreground-primary">
Correct answers: {correctAnswerCount}
</p>
)}
</div>
<Quiz questions={questions} disabled={disabled} />
<Spacer size="m" />
<Button onClick={handleSubmit}>Submit</Button>
</div>
);
};

export const Default: Story = {
render: QuizDefault,
args: {},
Expand Down Expand Up @@ -471,4 +576,123 @@ const App = () => {
},
};

export const WithCorrectAnswersShownOnSuccess: Story = {
render: QuizWithCorrectAnswersShownOnSuccess,
args: {},
parameters: {
docs: {
source: {
code: `
import { Quiz, useQuiz, Button, Spacer } from '@freecodecamp/ui';

const initialQuestions = [
{
question: "Lorem ipsum dolor sit amet",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={\`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>\`}
getCodeBlockAriaLabel={(codeName) => \`\${codeName} code example\`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 1,
},
{
question: "Consectetur adipiscing elit",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={\`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>\`}
getCodeBlockAriaLabel={(codeName) => \`\${codeName} code example\`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 2,
},
{
question: "Fugit itaque delectus voluptatem alias aliquid",
answers: [
{
label: "Option 1",
value: 1,
feedback: (
<PrismFormatted
text={\`<p>Quaerat in autem sapiente illum. Vel mollitia omnis qui dolorem <code>um</code> esse eos maiores possimus. Est laborum quam aliquam qui sunt. Ut ea et qui provident voluptatibus. Eius quam odit sint cumque sint. Corporis quia et dicta.</p>\`}
getCodeBlockAriaLabel={(codeName) => \`\${codeName} code example\`}
/>
),
},
{
label: "Option 2",
value: 2,
feedback:
"Recusandae necessitatibus consequatur voluptatem sapiente.",
},
{ label: "Option 3", value: 3, feedback: "Voluptas et et animi quo." },
],
correctAnswer: 3,
},
];

const App = () => {
const { questions, validateAnswers } = useQuiz({
initialQuestions,
validationMessages: {
correct: "Correct.",
incorrect: "Incorrect.",
},
passingPercent: 50,
showCorrectAnswersOnSuccess: true
});

const [disabled, setDisabled] = useState(false);

const handleSubmit = () => {
validateAnswers();
setDisabled(true);
};

return (
<div>
<div aria-live="polite">
{!!correctAnswerCount && (
<p className="text-foreground-primary">
Correct answers: {correctAnswerCount}
</p>
)}
</div>
<Quiz questions={questions} disabled={disabled} />
<Spacer size="m" />
<Button onClick={handleSubmit}>Submit</Button>
</div>
);
};`,
},
},
},
};

export default story;
99 changes: 99 additions & 0 deletions src/quiz/use-quiz.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,105 @@ describe("useQuiz", () => {
expect(result.current.validated).toBe(true);
});

it("should return the questions array with the correct validation status if `showCorrectAnswersOnSuccess` is `true`", () => {
const { result } = renderHook(() =>
useQuiz({
initialQuestions: [
{
question: "Lorem ipsum dolor sit amet",
answers: [
{ label: "Option 1", value: 1 },
{ label: "Option 2", value: 2 },
{ label: "Option 3", value: 3 },
],
selectedAnswer: 1,
correctAnswer: 1,
},
{
question: "Consectetur adipiscing elit",
answers: [
{ label: "Option 1", value: 1 },
{ label: "Option 2", value: 2 },
{ label: "Option 3", value: 3 },
],
selectedAnswer: 3,
correctAnswer: 2,
},
],
validationMessages,
passingPercent: 50,
showCorrectAnswersOnSuccess: true,
}),
);

expect(result.current.validated).toBe(false);
expect(result.current.correctAnswerCount).toBeUndefined();
expect(result.current.grade).toBeUndefined();

act(() => {
result.current.validateAnswers();
});

expect(result.current.questions).toMatchObject([
{
question: "Lorem ipsum dolor sit amet",
answers: [
{
label: "Option 1",
value: 1,
validation: {
message: "Correct",
state: "correct",
},
},
{
label: "Option 2",
value: 2,
},
{
label: "Option 3",
value: 3,
},
],
onChange: expect.any(Function),
selectedAnswer: 1,
correctAnswer: 1,
},
{
question: "Consectetur adipiscing elit",
answers: [
{
label: "Option 1",
value: 1,
},
{
label: "Option 2",
value: 2,
validation: {
message: "Correct",
state: "correct",
},
},
{
label: "Option 3",
value: 3,
validation: {
message: "Incorrect",
state: "incorrect",
},
},
],
onChange: expect.any(Function),
selectedAnswer: 3,
correctAnswer: 2,
},
]);

expect(result.current.correctAnswerCount).toBe(1);
expect(result.current.grade).toBe(50);
expect(result.current.validated).toBe(true);
});

it("should call the `onSuccess` function if the quiz results meet the passing grade", () => {
const onSuccess = jest.fn();
const onFailure = jest.fn();
Expand Down
Loading