diff --git a/public/tasks/task-components.md b/public/tasks/task-components.md
new file mode 100644
index 0000000000..a6ff48694d
--- /dev/null
+++ b/public/tasks/task-components.md
@@ -0,0 +1,5 @@
+# Task - Components
+
+Version: 0.0.1
+
+Fix some components that are using state incorrectly.
diff --git a/src/App.tsx b/src/App.tsx
index 01853915cc..1eb3d1a521 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -9,13 +9,18 @@ import { StartAttempt } from "./components/StartAttempt";
import { TwoDice } from "./components/TwoDice";
import { CycleHoliday } from "./components/CycleHoliday";
import { Counter } from "./components/Counter";
+
+import { DoubleHalf } from "./bad-components/DoubleHalf";
+import { ColoredBox } from "./bad-components/ColoredBox";
+import { ShoveBox } from "./bad-components/ShoveBox";
+import { ChooseTeam } from "./bad-components/ChooseTeam";
+
function App(): JSX.Element {
return (
UD CISC275 with React Hooks and TypeScript
- HEAD
Software Development Task 3
+
+
+
+
+
+
+
+
@@ -55,7 +68,6 @@ function App(): JSX.Element {
- upstream/task-state
);
}
diff --git a/src/bad-components/ChooseTeam.test.tsx b/src/bad-components/ChooseTeam.test.tsx
new file mode 100644
index 0000000000..f11465a2d6
--- /dev/null
+++ b/src/bad-components/ChooseTeam.test.tsx
@@ -0,0 +1,61 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { ChooseTeam } from "./ChooseTeam";
+
+describe("ChooseTeam Component tests", () => {
+ beforeEach(() => {
+ render();
+ });
+ test("The initial team is empty", () => {
+ const currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(0);
+ });
+ test("There are 7 buttons.", () => {
+ const adders = screen.queryAllByRole("button");
+ expect(adders).toHaveLength(7);
+ });
+ test("Clicking first team member works", () => {
+ const first = screen.queryAllByRole("button")[0];
+ first.click();
+ const currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(1);
+ expect(currentTeam[0].textContent).toEqual(first.textContent);
+ });
+ test("Clicking the third team member works", () => {
+ const third = screen.queryAllByRole("button")[2];
+ third.click();
+ const currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(1);
+ expect(currentTeam[0].textContent).toEqual(third.textContent);
+ });
+ test("Clicking three team members works", () => {
+ const [, second, third, , fifth] = screen.queryAllByRole("button");
+ third.click();
+ second.click();
+ fifth.click();
+ const currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(3);
+ expect(currentTeam[0].textContent).toEqual(third.textContent);
+ expect(currentTeam[1].textContent).toEqual(second.textContent);
+ expect(currentTeam[2].textContent).toEqual(fifth.textContent);
+ });
+ test("Clearing the team works", () => {
+ const [, second, third, fourth, fifth, , clear] =
+ screen.queryAllByRole("button");
+ third.click();
+ second.click();
+ fifth.click();
+ let currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(3);
+ expect(currentTeam[0].textContent).toEqual(third.textContent);
+ expect(currentTeam[1].textContent).toEqual(second.textContent);
+ expect(currentTeam[2].textContent).toEqual(fifth.textContent);
+ clear.click();
+ currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(0);
+ fourth.click();
+ currentTeam = screen.queryAllByRole("listitem");
+ expect(currentTeam).toHaveLength(1);
+ expect(currentTeam[0].textContent).toEqual(fourth.textContent);
+ });
+});
diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx
new file mode 100644
index 0000000000..8b7b7fd564
--- /dev/null
+++ b/src/bad-components/ChooseTeam.tsx
@@ -0,0 +1,54 @@
+import React, { useState } from "react";
+import { Button, Row, Col } from "react-bootstrap";
+
+const PEOPLE = [
+ "Alan Turing",
+ "Grace Hopper",
+ "Ada Lovelace",
+ "Charles Babbage",
+ "Barbara Liskov",
+ "Margaret Hamilton"
+];
+
+export function ChooseTeam(): JSX.Element {
+ const [allOptions, setAllOptions] = useState(PEOPLE);
+ const [team, setTeam] = useState([]);
+
+ function chooseMember(newMember: string) {
+ if (!team.includes(newMember)) {
+ setTeam([...team, newMember]);
+ }
+ }
+
+ function clearTeam() {
+ setTeam([]);
+ }
+
+ return (
+
+
Choose Team
+
+
+ {allOptions.map((option: string) => (
+
+ Add{" "}
+
+
+ ))}
+
+
+ Team:
+ {team.map((member: string) => (
+ {member}
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/bad-components/ColoredBox.test.tsx b/src/bad-components/ColoredBox.test.tsx
new file mode 100644
index 0000000000..c17a13f66c
--- /dev/null
+++ b/src/bad-components/ColoredBox.test.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { ColoredBox } from "./ColoredBox";
+
+describe("ColoredBox Component tests", () => {
+ beforeEach(() => {
+ render();
+ });
+ test("The ColoredBox is initially red.", () => {
+ const box = screen.getByTestId("colored-box");
+ expect(box).toHaveStyle({ backgroundColor: "red" });
+ });
+ test("There is a button", () => {
+ expect(screen.getByRole("button")).toBeInTheDocument();
+ });
+ test("Clicking the button advances the color.", () => {
+ const nextColor = screen.getByRole("button");
+ nextColor.click();
+ expect(screen.getByTestId("colored-box")).toHaveStyle({
+ backgroundColor: "blue"
+ });
+ nextColor.click();
+ expect(screen.getByTestId("colored-box")).toHaveStyle({
+ backgroundColor: "green"
+ });
+ nextColor.click();
+ expect(screen.getByTestId("colored-box")).toHaveStyle({
+ backgroundColor: "red"
+ });
+ });
+});
diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx
new file mode 100644
index 0000000000..33cddbb219
--- /dev/null
+++ b/src/bad-components/ColoredBox.tsx
@@ -0,0 +1,45 @@
+import React, { useState } from "react";
+import { Button } from "react-bootstrap";
+
+export const COLORS = ["red", "blue", "green"];
+const DEFAULT_COLOR_INDEX = 0;
+
+function ChangeColor(): JSX.Element {
+ const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX);
+ return (
+
+ );
+}
+
+function ColorPreview({ colorIndex }: { colorIndex: number }): JSX.Element {
+ return (
+
+ );
+}
+
+export function ColoredBox(): JSX.Element {
+ const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX);
+
+ return (
+
+
Colored Box
+
The current color is: {COLORS[colorIndex]}
+
+
+
+
+
+ );
+}
diff --git a/src/bad-components/DoubleHalf.test.tsx b/src/bad-components/DoubleHalf.test.tsx
new file mode 100644
index 0000000000..cbae5f68af
--- /dev/null
+++ b/src/bad-components/DoubleHalf.test.tsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { DoubleHalf } from "./DoubleHalf";
+
+describe("DoubleHalf Component tests", () => {
+ beforeEach(() => {
+ render();
+ });
+ test("The DoubleHalf value is initially 10", () => {
+ expect(screen.getByText("10")).toBeInTheDocument();
+ expect(screen.queryByText("20")).not.toBeInTheDocument();
+ expect(screen.queryByText("5")).not.toBeInTheDocument();
+ });
+ test("There are Double and Halve buttons", () => {
+ expect(
+ screen.getByRole("button", { name: /Double/i })
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: /Halve/i })
+ ).toBeInTheDocument();
+ });
+ test("You can double the number.", () => {
+ const double = screen.getByRole("button", { name: /Double/i });
+ double.click();
+ expect(screen.getByText("20")).toBeInTheDocument();
+ expect(screen.queryByText("10")).not.toBeInTheDocument();
+ });
+ test("You can halve the number.", () => {
+ const halve = screen.getByRole("button", { name: /Halve/i });
+ halve.click();
+ expect(screen.getByText("5")).toBeInTheDocument();
+ expect(screen.queryByText("10")).not.toBeInTheDocument();
+ });
+ test("You can double AND halve the number.", () => {
+ const double = screen.getByRole("button", { name: /Double/i });
+ const halve = screen.getByRole("button", { name: /Halve/i });
+ double.click();
+ expect(screen.getByText("20")).toBeInTheDocument();
+ expect(screen.queryByText("10")).not.toBeInTheDocument();
+ double.click();
+ expect(screen.getByText("40")).toBeInTheDocument();
+ expect(screen.queryByText("20")).not.toBeInTheDocument();
+ halve.click();
+ expect(screen.getByText("20")).toBeInTheDocument();
+ expect(screen.queryByText("10")).not.toBeInTheDocument();
+ halve.click();
+ expect(screen.getByText("10")).toBeInTheDocument();
+ expect(screen.queryByText("20")).not.toBeInTheDocument();
+ halve.click();
+ expect(screen.getByText("5")).toBeInTheDocument();
+ expect(screen.queryByText("10")).not.toBeInTheDocument();
+ halve.click();
+ expect(screen.getByText("2.5")).toBeInTheDocument();
+ expect(screen.queryByText("5")).not.toBeInTheDocument();
+ });
+});
diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx
new file mode 100644
index 0000000000..ed6c3a6681
--- /dev/null
+++ b/src/bad-components/DoubleHalf.tsx
@@ -0,0 +1,26 @@
+import React, { useState } from "react";
+import { Button } from "react-bootstrap";
+//import { dhValue, setDhValue } from "./DoubleHalfState";
+
+export function DoubleHalf(): JSX.Element {
+ const [dhValue, setDhValue] = useState(10);
+
+ function Doubler() {
+ setDhValue(2 * dhValue);
+ }
+
+ function Halver() {
+ setDhValue(0.5 * dhValue);
+ }
+
+ return (
+
+
Double Half
+
+ The current value is: {dhValue}
+
+
+
+
+ );
+}
diff --git a/src/bad-components/ShoveBox.test.tsx b/src/bad-components/ShoveBox.test.tsx
new file mode 100644
index 0000000000..2adec13d4e
--- /dev/null
+++ b/src/bad-components/ShoveBox.test.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { ShoveBox } from "./ShoveBox";
+
+describe("ShoveBox Component tests", () => {
+ beforeEach(() => {
+ render();
+ });
+ test("The MoveableBox is initially nearby.", () => {
+ const box = screen.getByTestId("moveable-box");
+ expect(box).toHaveStyle({ marginLeft: "10px" });
+ });
+ test("There is a button", () => {
+ expect(screen.getByRole("button")).toBeInTheDocument();
+ });
+ test("Clicking the button moves the box.", () => {
+ const shoveBox = screen.getByRole("button");
+ shoveBox.click();
+ expect(screen.getByTestId("moveable-box")).toHaveStyle({
+ marginLeft: "14px"
+ });
+ shoveBox.click();
+ expect(screen.getByTestId("moveable-box")).toHaveStyle({
+ marginLeft: "18px"
+ });
+ shoveBox.click();
+ expect(screen.getByTestId("moveable-box")).toHaveStyle({
+ marginLeft: "22px"
+ });
+ });
+});
diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx
new file mode 100644
index 0000000000..ed5a515735
--- /dev/null
+++ b/src/bad-components/ShoveBox.tsx
@@ -0,0 +1,49 @@
+import React, { useState } from "react";
+import { Button } from "react-bootstrap";
+
+function ShoveBoxButton({
+ position,
+ setPosition
+}: {
+ position: number;
+ setPosition: (newPosition: number) => void;
+}) {
+ return (
+
+ );
+}
+
+function MoveableBox({ position }: { position: number }): JSX.Element {
+ return (
+
+ );
+}
+
+export function ShoveBox(): JSX.Element {
+ const [position, setPosition] = useState(10);
+
+ return (
+
+
Shove Box
+
The box is at: {MoveableBox}
+
+
+
+
+
+ );
+}