diff --git a/src/App.tsx b/src/App.tsx index c382a9a262..11985828e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -49,7 +49,6 @@ function App(): JSX.Element {
-

diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx index 4721314964..1498dcefb2 100644 --- a/src/bad-components/ChooseTeam.tsx +++ b/src/bad-components/ChooseTeam.tsx @@ -11,17 +11,7 @@ const PEOPLE = [ ]; export function ChooseTeam(): JSX.Element { - const [allOptions] = useState(PEOPLE); - const [team, setTeam] = useState([]); - function chooseMember(newMember: string) { - if (!team.includes(newMember)) { - setTeam([...team, newMember]); - } - } - - function clearTeam() { - setTeam([]); } return ( @@ -32,10 +22,7 @@ export function ChooseTeam(): JSX.Element { {allOptions.map((option: string) => (
Add{" "} -
diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index fe3c00b788..bd5057145e 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -7,6 +7,14 @@ const DEFAULT_COLOR_INDEX = 0; function ChangeColor(): JSX.Element { const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX); + return ( + + ); +} + +function ColorPreview(): JSX.Element { const handleNextColorClick = () => { setColorIndex((colorIndex + 1) % COLORS.length); }; @@ -21,7 +29,11 @@ function ColorPreview({ colorIndex }: { colorIndex: number }) { style={{ width: "50px", height: "50px", + + backgroundColor: COLORS[DEFAULT_COLOR_INDEX], + backgroundColor: COLORS[colorIndex], + display: "inline-block", verticalAlign: "bottom", marginLeft: "5px" @@ -31,6 +43,15 @@ function ColorPreview({ colorIndex }: { colorIndex: number }) { } export function ColoredBox(): JSX.Element { + + return ( +
+

Colored Box

+ The current color is: {COLORS[DEFAULT_COLOR_INDEX]} +
+ + + const [colorIndex] = useState(DEFAULT_COLOR_INDEX); return ( diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx index 3796a95573..a9808debad 100644 --- a/src/bad-components/DoubleHalf.tsx +++ b/src/bad-components/DoubleHalf.tsx @@ -1,6 +1,19 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; +import { dhValue, setDhValue } from "./DoubleHalfState"; + +function Doubler(): JSX.Element { + return ; +} + +function Halver(): JSX.Element { + return ; +} + +export function DoubleHalf(): JSX.Element { + + export const [dhValue, setDhValue] = useState(10); export function DoubleHalf(): JSX.Element { @@ -18,8 +31,13 @@ export function DoubleHalf(): JSX.Element {
The current value is: {dhValue}
+ + + + +
); } diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx index 862cb30537..2406e09c0b 100644 --- a/src/bad-components/ShoveBox.tsx +++ b/src/bad-components/ShoveBox.tsx @@ -8,6 +8,14 @@ function ShoveBoxButton({ position: number; setPosition: (newPosition: number) => void; }) { + + return ( + + ); +} + +function MoveableBox(): JSX.Element { + const [position, setPosition] = useState(10); const handleShoveClick = () => { setPosition(position + 4); }; @@ -16,6 +24,7 @@ function ShoveBoxButton({ } function MoveableBox({ position }: { position: number }) { + return (
(10); return (

Shove Box

+ {/* The box is at: {box.position} +
+ + {box} +
*/} {} diff --git a/src/form-components/ChangeColor.test.tsx b/src/form-components/ChangeColor.test.tsx new file mode 100644 index 0000000000..d74ba37243 --- /dev/null +++ b/src/form-components/ChangeColor.test.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ChangeColor } from "./ChangeColor"; + +describe("ChangeColor Component tests", () => { + beforeEach(() => render()); + test("There are at least 8 radio buttons and the colored box", () => { + const radios = screen.getAllByRole("radio"); + expect(radios.length).toBeGreaterThanOrEqual(8); + expect(screen.getByTestId("colored-box")).toBeInTheDocument(); + }); + test("Switching the color switches the displayed color.", () => { + const radios: HTMLInputElement[] = screen.getAllByRole("radio"); + // Switch to first + radios[0].click(); + let coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[0].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); + // Switch to third + radios[2].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[2].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[2].value }); + // Switch to 8th + radios[7].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[7].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[7].value }); + // Switch back to first + radios[0].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[0].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); + }); +}); diff --git a/src/form-components/ChangeColor.tsx b/src/form-components/ChangeColor.tsx new file mode 100644 index 0000000000..ad0fcba051 --- /dev/null +++ b/src/form-components/ChangeColor.tsx @@ -0,0 +1,51 @@ +import React, { useState } from "react"; + +export function ChangeColor(): JSX.Element { + const [selectedColor, setSelectedColor] = useState(""); + const colors = [ + "red", + "blue", + "green", + "yellow", + "purple", + "orange", + "pink", + "brown" + ]; + const handleColorChange = (event: { + target: { value: React.SetStateAction }; + }) => { + setSelectedColor(event.target.value); + }; + + return ( +
+

Change Color

+
+ {colors.map((color, index) => ( + + ))} +
+
+ {selectedColor || "Select a color"} +
+
+ ); +} diff --git a/src/form-components/CheckAnswer.test.tsx b/src/form-components/CheckAnswer.test.tsx new file mode 100644 index 0000000000..076ab209a7 --- /dev/null +++ b/src/form-components/CheckAnswer.test.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { CheckAnswer } from "./CheckAnswer"; +import userEvent from "@testing-library/user-event"; + +describe("CheckAnswer Component tests", () => { + test("There is an input box", () => { + render(); + const inputBox = screen.getByRole("textbox"); + expect(inputBox).toBeInTheDocument(); + }); + test("The answer is originally incorrect.", () => { + render(); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Entering the right answer makes it correct.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "42"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Entering the wrong answer makes it incorrect.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "43"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Entering a different right answer makes it correct.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "Hello"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Entering a different wrong answer still makes it incorrect.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "42"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); +}); diff --git a/src/form-components/CheckAnswer.tsx b/src/form-components/CheckAnswer.tsx new file mode 100644 index 0000000000..b4e886a6ed --- /dev/null +++ b/src/form-components/CheckAnswer.tsx @@ -0,0 +1,26 @@ +import React, { useState } from "react"; + +export function CheckAnswer({ + expectedAnswer +}: { + expectedAnswer: string; +}): JSX.Element { + const [userAnswer, setUserAnswer] = useState(""); + const handleInputChange = (event: { + target: { value: React.SetStateAction }; + }) => { + setUserAnswer(event.target.value); + }; + + return ( +
+ + + {userAnswer === expectedAnswer ? ✔️ : } +
+ ); +} diff --git a/src/form-components/EditMode.test.tsx b/src/form-components/EditMode.test.tsx new file mode 100644 index 0000000000..b2f2a43a36 --- /dev/null +++ b/src/form-components/EditMode.test.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { EditMode } from "./EditMode"; +import userEvent from "@testing-library/user-event"; + +describe("EditMode Component tests", () => { + beforeEach(() => render()); + test("There is one checkbox and no textboxes", () => { + const switchButton = screen.getByRole("checkbox"); + expect(switchButton).toBeInTheDocument(); + expect(switchButton.parentElement).toHaveClass("form-switch"); + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + }); + test("Initial text should be 'Your Name is a student'.", () => { + expect(screen.getByText(/Your Name is a student/i)).toBeInTheDocument(); + }); + test("Can switch into Edit Mode", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + expect(screen.getByRole("textbox")).toBeInTheDocument(); + expect(screen.getAllByRole("checkbox")).toHaveLength(2); + }); + test("Editing the name and student status changes the text", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + const nameBox = screen.getByRole("textbox"); + userEvent.type(nameBox, "Ada Lovelace"); + const studentBox = screen.getByRole("checkbox", { name: /student/i }); + studentBox.click(); + switchButton.click(); + expect( + screen.getByText(/Ada Lovelace is not a student/i) + ).toBeInTheDocument(); + }); + test("Different name, click student box twice changes the text", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + const nameBox = screen.getByRole("textbox"); + userEvent.type(nameBox, "Alan Turing"); + const studentBox = screen.getByRole("checkbox", { name: /student/i }); + studentBox.click(); + studentBox.click(); + switchButton.click(); + expect( + screen.getByText(/Alan Turing is a student/i) + ).toBeInTheDocument(); + }); +}); diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx new file mode 100644 index 0000000000..391208db07 --- /dev/null +++ b/src/form-components/EditMode.tsx @@ -0,0 +1,61 @@ +import React, { useState } from "react"; + +export function EditMode(): JSX.Element { + const [editMode, setEditMode] = useState(false); + const [userName, setUserName] = useState("Your Name"); + const [isStudent, setIsStudent] = useState(true); + + const handleEditModeToggle = () => { + setEditMode(!editMode); + }; + const handleUserNameChange = (event: { + target: { value: React.SetStateAction }; + }) => { + setUserName(event.target.value); + }; + + const handleStudentCheckboxChange = () => { + setIsStudent(!isStudent); + }; + return ( +
+

Edit Mode

+
+ + +
+ {editMode ? ( +
+ + + +
+ ) : ( +
+

+ {userName} is{" "} + {isStudent ? "a student" : "not a student"} +

+
+ )} +
+ ); +} diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx new file mode 100644 index 0000000000..eb1c3e4a45 --- /dev/null +++ b/src/form-components/GiveAttempts.test.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { fireEvent, render, screen } from "@testing-library/react"; +import { GiveAttempts } from "./GiveAttempts"; +import userEvent from "@testing-library/user-event"; + +describe("GiveAttempts Component tests", () => { + beforeEach(() => { + render(); + }); + + test("There is a number entry box and two buttons", () => { + expect(screen.getByRole("spinbutton")).toBeInTheDocument(); + expect(screen.getAllByRole("button")).toHaveLength(2); + }); + + test("There is are initially 3 attempts", () => { + expect(screen.getByText(/3/i)).toBeInTheDocument(); + }); + + test("You can use attempts", () => { + const use = screen.getByRole("button", { name: /use/i }); + use.click(); + expect(screen.getByText(/2/i)).toBeInTheDocument(); + use.click(); + use.click(); + expect(screen.getByText(/0/i)).toBeInTheDocument(); + expect(use).toBeDisabled(); + }); + test("You can gain arbitrary attempts", () => { + const gain = screen.getByRole("button", { name: /gain/i }); + const amountToGive = screen.getByRole("spinbutton"); + userEvent.type(amountToGive, "10"); + gain.click(); + expect(screen.getByText(/13/i)).toBeInTheDocument(); + userEvent.type(amountToGive, "100"); + gain.click(); + expect(screen.getByText(/113/i)).toBeInTheDocument(); + }); + test("Cannot gain blank amounts", () => { + const gain = screen.getByRole("button", { name: /gain/i }); + const amountToGive = screen.getByRole("spinbutton"); + fireEvent.change(amountToGive, { target: { value: "" } }); + gain.click(); + expect(screen.getByText(/3/i)).toBeInTheDocument(); + }); +}); diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx new file mode 100644 index 0000000000..9952d7e5d9 --- /dev/null +++ b/src/form-components/GiveAttempts.tsx @@ -0,0 +1,43 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +export function GiveAttempts(): JSX.Element { + const [attemptsLeft, setAttemptsLeft] = useState(3); + const [requestedAttempts, setRequestedAttempts] = useState(""); + + const handleUseAttempt = () => { + if (attemptsLeft > 0) { + setAttemptsLeft(attemptsLeft - 1); + } + }; + + const handleGainAttempts = () => { + const parsedAttempts = parseInt(requestedAttempts, 10); + + if (!isNaN(parsedAttempts) && parsedAttempts >= 0) { + setAttemptsLeft(attemptsLeft + parsedAttempts); + setRequestedAttempts(""); + } + }; + return ( +
+

Give Attempts

+

Attempts Left: {attemptsLeft}

+
+ + setRequestedAttempts(e.target.value)} + /> + + +
+
+ ); +} diff --git a/src/form-components/MultipleChoiceQuestion.test.tsx b/src/form-components/MultipleChoiceQuestion.test.tsx new file mode 100644 index 0000000000..03a520a818 --- /dev/null +++ b/src/form-components/MultipleChoiceQuestion.test.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { MultipleChoiceQuestion } from "./MultipleChoiceQuestion"; +import userEvent from "@testing-library/user-event"; + +describe("MultipleChoiceQuestion Component tests", () => { + test("There is a select box", () => { + render( + + ); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + test("The answer is initially incorrect", () => { + render( + + ); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Can choose the correct answer", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "2"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Can choose the correct answer and then incorrect", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "2"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + userEvent.selectOptions(select, "3"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Can start off initially correct", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "Alpha"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("One more test of choosing the right answer", () => { + render( + + ); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "World"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); +}); diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx new file mode 100644 index 0000000000..176189ae3c --- /dev/null +++ b/src/form-components/MultipleChoiceQuestion.tsx @@ -0,0 +1,35 @@ +import React, { useState } from "react"; + +export function MultipleChoiceQuestion({ + options, + expectedAnswer +}: { + options: string[]; + expectedAnswer: string; +}): JSX.Element { + const [selectedChoice, setSelectedChoice] = useState(options[0]); + + const handleChoiceSelect = (event: { + target: { value: React.SetStateAction }; + }) => { + setSelectedChoice(event.target.value); + }; + return ( +
+

Multiple Choice Question

+ + + {selectedChoice === expectedAnswer ? ( + ✔️ + ) : ( + + )} +
+ ); +}