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} +
    + + +
    +
    + ); +}