Skip to content
This repository was archived by the owner on Sep 25, 2025. It is now read-only.
Closed
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
11 changes: 5 additions & 6 deletions e2e/can-run-quiz.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,18 @@ test("question page should contain 4 options and `submit` button", async ({
await page.getByRole("button", { name: "Submit", exact: true }).click();
});

test("selected option must have 'answers-btns--selected' class", async ({
page
}) => {
test("selected first option must be checked", async ({ page }) => {
await page.getByRole("button", { name: "HTML" }).click();

await page.getByRole("button", { name: "10", exact: true }).click();

const firstOptionText = await page.getByRole("button").first().textContent();
// Select the first option (no matter if it's right or wrong)
await page.getByRole("button").first().click();

await expect(page.getByRole("button").first()).toHaveClass(
/answers-btns--selected/
);
// Check if the first radio is checked
const hiddenRadioButton = page.locator(`input[id='${firstOptionText}']`);
await expect(hiddenRadioButton).toBeChecked();
});

test("should show a modal after selecting one option and click the `submit` button", async ({
Expand Down
2 changes: 1 addition & 1 deletion e2e/score-and-results.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ test("should show 'success' modal after selecting the correct option", async ({
const questionData = await getQuestionData(question || "");

const answer = questionData.Answer;
await page.getByRole("button", { name: answer }).click();
await page.getByRole("button", { name: answer, exact: true }).click();
await page.getByRole("button", { name: "Submit", exact: true }).click();

await verifyModalResponse(page, correctModalResponses, "1");
Expand Down
19 changes: 19 additions & 0 deletions src/__tests__/Selectable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import Selectable from "../components/Selectable";

import { render, cleanup } from "@testing-library/react";
import { vi } from "vitest";
import { expect, afterEach, describe, it } from "vitest";

afterEach(cleanup);
describe("Selectable", () => {
it("Displays the passed options", () => {
const ops = ["one", "two", "three"];
const { getByLabelText } = render(
<Selectable options={ops} groupName="someGroup" onChange={vi.fn()} />
);
expect(getByLabelText("one").textContent).toBeDefined();
expect(getByLabelText("two").textContent).toBeDefined();
expect(getByLabelText("three").textContent).toBeDefined();
});
});
32 changes: 32 additions & 0 deletions src/components/Selectable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import "../stylesheets/Selectable.css";
const Selectable = ({
options,
groupName,
onChange
}: {
options: string[];
groupName: string;
onChange: (val: string) => void;
}) => {
return (
<ul className="select-btn-div">
{options.map((itm: string) => (
<li key={itm}>
<label className={"select-btns"} htmlFor={itm} role="button">
<input
type="radio"
id={itm}
name={groupName}
value={itm}
onChange={e => onChange(e.target.value)}
/>

{itm}
</label>
</li>
))}
</ul>
);
};
export default Selectable;
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export const CATEGORIES = [
"IT",
"Linux",
"Python",
"SQL"
"SQL",
"Random"
];

export const ALL_CATEGORIES = [
Expand Down
65 changes: 32 additions & 33 deletions src/pages/Questions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import QuizModal from "../components/QuizModal";
import React, { useEffect } from "react";

import { QuizProps } from "../types";
import Selectable from "../components/Selectable";

const Questions: React.FC<QuizProps> = QuizProps => {
const navigate = useNavigate();
Expand All @@ -20,43 +21,41 @@ const Questions: React.FC<QuizProps> = QuizProps => {
<p>Points: {QuizProps.points}</p>
</div>
<h1 className="quiz-heading">Question {QuizProps.questionNumber}</h1>
<div className="quiz-div">
<form className="select-quiz-styles">
{QuizProps.chooseAnswer ? (
<QuizModal {...QuizProps.modalProps} />
) : (
<fieldset className="quiz-answers-div">
<legend>
<span className="sr-only">
Question {QuizProps.questionNumber}
</span>
{QuizProps.currQuestion.Question}
</legend>
<ul>
{QuizProps.choicesArr.length > 0 &&
QuizProps.choicesArr[QuizProps.questionNumber - 1].map(
(choice: string, index: number) => (
<li key={index}>
<button
className={`answers-btns ${choice === QuizProps.selectedOption ? `answers-btns--selected` : ``}`}
onClick={() => QuizProps.selectOption(choice)}
>
{choice}
</button>
</li>
)
)}
</ul>
<hr />
<button
className="select-btns submit-btn"
style={{ opacity: QuizProps.selectedOption ? 1 : 0.5 }}
onClick={() => QuizProps.checkAnswer()}
>
Submit
</button>
</fieldset>
<>
<fieldset className="quiz-answers-div">
<legend>
<span className="sr-only">
Question {QuizProps.questionNumber}
</span>
{QuizProps.currQuestion.Question}
</legend>
</fieldset>

<Selectable
options={QuizProps.choicesArr[QuizProps.questionNumber - 1]}
groupName="answers"
onChange={(choice: string) => {
QuizProps.selectOption(choice);
}}
/>

<div className="select-btn-div">
<hr />
<button
className="select-btns submit-btn"
style={{ opacity: QuizProps.selectedOption ? 1 : 0.5 }}
onClick={() => QuizProps.checkAnswer()}
>
Submit
</button>
</div>
</>
)}
</div>
</form>
</>
);
};
Expand Down
27 changes: 10 additions & 17 deletions src/pages/SelectCategory.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import React from "react";
import { CATEGORIES } from "../constants";
import { SelectCategoryProps } from "../types";
import Selectable from "../components/Selectable";

const SelectCategory: React.FC<SelectCategoryProps> = SelectCategoryProps => {
return (
<div className="select-quiz-styles">
<h2 className="quiz-heading">Choose a Category</h2>
<div className="select-btn-div">
{CATEGORIES.map((category: string, index: number) => (
<button
className="select-btns"
onClick={() => SelectCategoryProps.selectQuiz(category, index)}
key={index}
>
{category}
</button>
))}
<button
className="select-btns"
onClick={SelectCategoryProps.startRandomQuiz}
>
Random
</button>
</div>
<Selectable
options={CATEGORIES}
groupName="categories"
onChange={category =>
category === "Random"
? SelectCategoryProps.startRandomQuiz()
: SelectCategoryProps.selectQuiz(category, 0)
}
/>
</div>
);
};
Expand Down
30 changes: 13 additions & 17 deletions src/pages/SelectQuestionsTotal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { QUESTION_NUMS } from "../constants";
import { SelectQuestionsTotalProps } from "../types";
import Selectable from "../components/Selectable";

const SelectQuestionsTotal: React.FC<SelectQuestionsTotalProps> = ({
totalQuestions,
Expand All @@ -9,28 +10,23 @@ const SelectQuestionsTotal: React.FC<SelectQuestionsTotalProps> = ({
const availableQuizLengths = QUESTION_NUMS.filter(
length => length <= totalQuestions
);
const ops: string[] = [
...availableQuizLengths.map(n => `${n}`), // convert list of nums to list of strings
`All (${totalQuestions})` // add "all" option
];

return (
<div className="select-quiz-styles">
<h2 className="quiz-heading">Choose a length for the Quiz</h2>
<div className="select-btn-div">
{availableQuizLengths.map((choice: number, index: number) => (
<button
className="select-btns"
onClick={() => startQuiz(choice)}
key={index}
>
{choice}
</button>
))}

<button
className="select-btns"
onClick={() => startQuiz(totalQuestions)}
>
All ({totalQuestions})
</button>
</div>
<Selectable
options={ops}
groupName="QuizLengths"
onChange={(choice: string) => {
const num_choice = Number(choice.replace(/\D/g, "")); // handle "All (120) case"
startQuiz(num_choice);
}}
/>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/stylesheets/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ hr {

@media screen and (min-width: 768px) {
.select-btn-div {
width: 25%;
width: 50%;
padding: 0;
}
}
14 changes: 14 additions & 0 deletions src/stylesheets/Selectable.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
label:has(input[type="radio"]:checked) {
background-color: var(--bg-answer-selected);
}
ul,
li {
list-style-type: none;
input[type="radio"] {
display: none;
}
label {
color: var(--bg-primary);
text-align: center;
}
}