Skip to content
This repository was archived by the owner on Feb 22, 2026. It is now read-only.

Commit ab1e9d2

Browse files
משה וילנרshootermv
authored andcommitted
refactor: use SelectOptions component
1 parent 32d9e25 commit ab1e9d2

9 files changed

Lines changed: 111 additions & 76 deletions

File tree

e2e/can-run-quiz.spec.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,18 @@ test("question page should contain 4 options and `submit` button", async ({
6363
await page.getByRole("button", { name: "Submit", exact: true }).click();
6464
});
6565

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

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

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

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

8180
test("should show a modal after selecting one option and click the `submit` button", async ({

e2e/score-and-results.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ test("should show 'success' modal after selecting the correct option", async ({
6868
const questionData = await getQuestionData(question || "");
6969

7070
const answer = questionData.Answer;
71-
await page.getByRole("button", { name: answer }).click();
71+
await page.getByRole("button", { name: answer, exact: true }).click();
7272
await page.getByRole("button", { name: "Submit", exact: true }).click();
7373

7474
await verifyModalResponse(page, correctModalResponses, "1");

src/components/Selectable.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from "react";
2+
3+
const Selectable = ({
4+
options,
5+
groupName,
6+
onChange
7+
}: {
8+
options: string[];
9+
groupName: string;
10+
onChange: (val: string) => void;
11+
}) => {
12+
return (
13+
<ul className="select-btn-div">
14+
{options.map((itm: string) => (
15+
<li key={itm}>
16+
<label className={"select-btns"} htmlFor={itm} role="button">
17+
<input
18+
type="radio"
19+
id={itm}
20+
name={groupName}
21+
value={itm}
22+
onChange={e => onChange(e.target.value)}
23+
/>
24+
25+
{itm}
26+
</label>
27+
</li>
28+
))}
29+
</ul>
30+
);
31+
};
32+
export default Selectable;

src/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export const CATEGORIES = [
3131
"IT",
3232
"Linux",
3333
"Python",
34-
"SQL"
34+
"SQL",
35+
"Random"
3536
];
3637

3738
export const ALL_CATEGORIES = [

src/pages/Questions.tsx

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import QuizModal from "../components/QuizModal";
33
import React, { useEffect } from "react";
44

55
import { QuizProps } from "../types";
6+
import Selectable from "../components/Selectable";
67

78
const Questions: React.FC<QuizProps> = QuizProps => {
89
const navigate = useNavigate();
@@ -20,43 +21,41 @@ const Questions: React.FC<QuizProps> = QuizProps => {
2021
<p>Points: {QuizProps.points}</p>
2122
</div>
2223
<h1 className="quiz-heading">Question {QuizProps.questionNumber}</h1>
23-
<div className="quiz-div">
24+
<form className="select-quiz-styles">
2425
{QuizProps.chooseAnswer ? (
2526
<QuizModal {...QuizProps.modalProps} />
2627
) : (
27-
<fieldset className="quiz-answers-div">
28-
<legend>
29-
<span className="sr-only">
30-
Question {QuizProps.questionNumber}
31-
</span>
32-
{QuizProps.currQuestion.Question}
33-
</legend>
34-
<ul>
35-
{QuizProps.choicesArr.length > 0 &&
36-
QuizProps.choicesArr[QuizProps.questionNumber - 1].map(
37-
(choice: string, index: number) => (
38-
<li key={index}>
39-
<button
40-
className={`answers-btns ${choice === QuizProps.selectedOption ? `answers-btns--selected` : ``}`}
41-
onClick={() => QuizProps.selectOption(choice)}
42-
>
43-
{choice}
44-
</button>
45-
</li>
46-
)
47-
)}
48-
</ul>
49-
<hr />
50-
<button
51-
className="select-btns submit-btn"
52-
style={{ opacity: QuizProps.selectedOption ? 1 : 0.5 }}
53-
onClick={() => QuizProps.checkAnswer()}
54-
>
55-
Submit
56-
</button>
57-
</fieldset>
28+
<>
29+
<fieldset className="quiz-answers-div">
30+
<legend>
31+
<span className="sr-only">
32+
Question {QuizProps.questionNumber}
33+
</span>
34+
{QuizProps.currQuestion.Question}
35+
</legend>
36+
</fieldset>
37+
38+
<Selectable
39+
options={QuizProps.choicesArr[QuizProps.questionNumber - 1]}
40+
groupName="answers"
41+
onChange={(choice: string) => {
42+
QuizProps.selectOption(choice);
43+
}}
44+
/>
45+
46+
<fieldset className="quiz-answers-div">
47+
<hr />
48+
<button
49+
className="select-btns submit-btn"
50+
style={{ opacity: QuizProps.selectedOption ? 1 : 0.5 }}
51+
onClick={() => QuizProps.checkAnswer()}
52+
>
53+
Submit
54+
</button>
55+
</fieldset>
56+
</>
5857
)}
59-
</div>
58+
</form>
6059
</>
6160
);
6261
};

src/pages/SelectCategory.tsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
import React from "react";
22
import { CATEGORIES } from "../constants";
33
import { SelectCategoryProps } from "../types";
4+
import Selectable from "../components/Selectable";
45

56
const SelectCategory: React.FC<SelectCategoryProps> = SelectCategoryProps => {
67
return (
78
<div className="select-quiz-styles">
89
<h2 className="quiz-heading">Choose a Category</h2>
9-
<div className="select-btn-div">
10-
{CATEGORIES.map((category: string, index: number) => (
11-
<button
12-
className="select-btns"
13-
onClick={() => SelectCategoryProps.selectQuiz(category, index)}
14-
key={index}
15-
>
16-
{category}
17-
</button>
18-
))}
19-
<button
20-
className="select-btns"
21-
onClick={SelectCategoryProps.startRandomQuiz}
22-
>
23-
Random
24-
</button>
25-
</div>
10+
<Selectable
11+
options={CATEGORIES}
12+
groupName="categories"
13+
onChange={category =>
14+
category === "Random"
15+
? SelectCategoryProps.startRandomQuiz()
16+
: SelectCategoryProps.selectQuiz(category, 0)
17+
}
18+
/>
2619
</div>
2720
);
2821
};

src/pages/SelectQuestionsTotal.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import { QUESTION_NUMS } from "../constants";
33
import { SelectQuestionsTotalProps } from "../types";
4+
import Selectable from "../components/Selectable";
45

56
const SelectQuestionsTotal: React.FC<SelectQuestionsTotalProps> = ({
67
totalQuestions,
@@ -13,24 +14,18 @@ const SelectQuestionsTotal: React.FC<SelectQuestionsTotalProps> = ({
1314
return (
1415
<div className="select-quiz-styles">
1516
<h2 className="quiz-heading">Choose a length for the Quiz</h2>
16-
<div className="select-btn-div">
17-
{availableQuizLengths.map((choice: number, index: number) => (
18-
<button
19-
className="select-btns"
20-
onClick={() => startQuiz(choice)}
21-
key={index}
22-
>
23-
{choice}
24-
</button>
25-
))}
2617

27-
<button
28-
className="select-btns"
29-
onClick={() => startQuiz(totalQuestions)}
30-
>
31-
All ({totalQuestions})
32-
</button>
33-
</div>
18+
<Selectable
19+
options={[
20+
...availableQuizLengths.map(n => `${n}`),
21+
`All (${totalQuestions})`
22+
]}
23+
groupName="QuizLengths"
24+
onChange={(choice: string) => {
25+
const num_choice = Number(choice.replace(/\D/g, ""));
26+
startQuiz(num_choice);
27+
}}
28+
/>
3429
</div>
3530
);
3631
};

src/stylesheets/App.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ hr {
159159

160160
@media screen and (min-width: 768px) {
161161
.select-btn-div {
162-
width: 25%;
162+
width: 50%;
163163
padding: 0;
164164
}
165165
}

src/stylesheets/Button.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,26 @@
3737
font-size: 25px;
3838
}
3939

40+
label:has(input[type="radio"]:checked) {
41+
background-color: var(--bg-answer-selected);
42+
}
43+
4044
@media screen and (max-width: 480px) {
4145
.large-btn {
4246
width: 200px;
4347
height: 75px;
4448
font-size: 22px;
4549
}
4650
}
51+
52+
ul,
53+
li {
54+
list-style-type: none;
55+
input[type="radio"] {
56+
display: none;
57+
}
58+
label {
59+
color: #0a0a23;
60+
text-align: center;
61+
}
62+
}

0 commit comments

Comments
 (0)