From fd4f4748a90ffe7fa16f96105a12bcdf7da2040b Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 10 May 2024 13:02:31 -0700 Subject: [PATCH 1/6] created modal --- src/components/Header.js | 8 ++---- src/components/NewModal.js | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/components/NewModal.js diff --git a/src/components/Header.js b/src/components/Header.js index d1c5449..83e55eb 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -1,9 +1,9 @@ import React from "react" import noteLogo from "../assets/note-logo.png" import addUser from "../assets/add-user.png" -import edit from "../assets/edit.png" import bin from "../assets/bin.png" import logOut from "../assets/logout.png" +import NewModal from "./NewModal" const Header = ({ signedOutUser }) => { const signOut = async () => { @@ -33,11 +33,7 @@ const Header = ({ signedOutUser }) => { alt="black graphic of a note and a pencil" className="mx-4 my-2 flex h-7 justify-start" /> - black graphic of a notepad +
{ + const [openModal, setOpenModal] = useState(true) + + return ( + <> + + setOpenModal(false)}> + New Note +
+
+ Make Note Public or Private +
+ + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+ + ) +} + +export default NewModal From 456a7722cc4d444dd7bfa7fcc6308616671a5df3 Mon Sep 17 00:00:00 2001 From: mark19242 Date: Mon, 13 May 2024 08:49:05 -0700 Subject: [PATCH 2/6] working code --- src/App.js | 29 ++++++++- src/components/Header.js | 4 +- src/components/NewModal.js | 122 +++++++++++++++++++++++++++++++------ src/components/SignIn.js | 4 +- 4 files changed, 135 insertions(+), 24 deletions(-) diff --git a/src/App.js b/src/App.js index 828450f..56e4a5b 100644 --- a/src/App.js +++ b/src/App.js @@ -13,6 +13,8 @@ const App = () => { const loggedInUser = localStorage.getItem("user") if (loggedInUser) { setUser(JSON.parse(loggedInUser)) + } else { + navigate("/") } }, []) @@ -20,14 +22,39 @@ const App = () => { setUser(userData) navigate("/main") } + const signedOutUser = () => { setUser(null) navigate("/") } + const createNote = async (newNote) => { + try { + const postResponse = await fetch("http://localhost:3000/notes", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(newNote) + }) + console.log(postResponse) + if (!postResponse.ok) { + throw new Error("Server responded with status: " + postResponse.status) + } + await postResponse.json() + } catch (error) { + console.error("Error in createNote:", error) + alert("Oops something went wrong: " + error.message) + } + } + return ( <> -
+
} /> {user && } />} diff --git a/src/components/Header.js b/src/components/Header.js index 83e55eb..744d222 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -5,7 +5,7 @@ import bin from "../assets/bin.png" import logOut from "../assets/logout.png" import NewModal from "./NewModal" -const Header = ({ signedOutUser }) => { +const Header = ({ signedOutUser, createNote, user }) => { const signOut = async () => { try { const signOutResponse = await fetch("http://localhost:3000/logout", { @@ -33,7 +33,7 @@ const Header = ({ signedOutUser }) => { alt="black graphic of a note and a pencil" className="mx-4 my-2 flex h-7 justify-start" /> - +
{ - const [openModal, setOpenModal] = useState(true) +const initialState = { + openModal: false, + title: "", + content: "", + public: true, + errors: { title: false, content: false } +} + +function reducer(state, action) { + switch (action.type) { + case "TOGGLE_MODAL": + return { ...state, openModal: !state.openModal } + case "SET_FIELD": + return { ...state, [action.field]: action.value } + case "SET_ERROR": + return { + ...state, + errors: { ...state.errors, [action.field]: action.value } + } + default: + throw new Error("Unhandled action type: " + action.type) + } +} + +const NewModal = ({ createNote, user }) => { + const [state, dispatch] = useReducer(reducer, initialState) + + const handleInputChange = (field, value) => { + dispatch({ type: "SET_FIELD", field, value }) + if (state.errors[field]) { + dispatch({ type: "SET_ERROR", field, value: false }) + } + } + + const handleFormSubmit = async (e) => { + e.preventDefault() + const { title, content, public: isPublic } = state + let errors = {} + if (!title) errors.title = true + if (!content) errors.content = true + + if (Object.keys(errors).length) { + for (let error in errors) { + dispatch({ type: "SET_ERROR", field: error, value: true }) + } + return + } + + if (user && user.id) { + await createNote({ + title, + content, + public: isPublic, + creator: user.id + }) + dispatch({ type: "TOGGLE_MODAL" }) // Close modal on successful submission + } else { + console.error("User data is not available") + } + } return ( <> - - setOpenModal(false)}> + dispatch({ type: "TOGGLE_MODAL" })} + > New Note -
+
Make Note Public or Private
- + handleInputChange("public", true)} + /> -
-
- + handleInputChange("public", false)} + />
@@ -35,18 +101,36 @@ const NewModal = () => {
- + handleInputChange("title", e.target.value)} + /> + {state.errors.title && ( +
Title is required
+ )}
- + handleInputChange("content", e.target.value)} + /> + {state.errors.content && ( +
Content is required
+ )}
- + diff --git a/src/components/SignIn.js b/src/components/SignIn.js index d21e3fa..2b14f6b 100644 --- a/src/components/SignIn.js +++ b/src/components/SignIn.js @@ -6,8 +6,8 @@ const SignIn = ({ setFormStatus, signedInUser }) => { const [error, setError] = useState(false) const preloadedValues = { - email: "rebecka@bahringer.example", - password: "yrL4YgmuQ" + email: "eda_ruecker@nicolas.test", + password: "FPjhxi8BeL7ov6Rl" } const { register, From 32d63edcef06683f86c56723c3785b86d1f477e4 Mon Sep 17 00:00:00 2001 From: mark19242 Date: Mon, 13 May 2024 09:31:08 -0700 Subject: [PATCH 3/6] lint --- src/components/NewModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NewModal.js b/src/components/NewModal.js index d0483ba..f17f754 100644 --- a/src/components/NewModal.js +++ b/src/components/NewModal.js @@ -57,7 +57,7 @@ const NewModal = ({ createNote, user }) => { public: isPublic, creator: user.id }) - dispatch({ type: "TOGGLE_MODAL" }) // Close modal on successful submission + dispatch({ type: "TOGGLE_MODAL" }) } else { console.error("User data is not available") } From 8f38216f8e44826de24574995e6ba98f75c52cc1 Mon Sep 17 00:00:00 2001 From: mark19242 Date: Mon, 13 May 2024 10:02:32 -0700 Subject: [PATCH 4/6] Modal Test Complete --- src/__tests__/Header.test.js | 2 - src/__tests__/NewModal.test.js | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/__tests__/NewModal.test.js diff --git a/src/__tests__/Header.test.js b/src/__tests__/Header.test.js index 277e113..1c35f26 100644 --- a/src/__tests__/Header.test.js +++ b/src/__tests__/Header.test.js @@ -12,8 +12,6 @@ test("renders the header component", () => { const headerLogo = screen.getByAltText(/black graphic of a note and a pencil/) expect(headerLogo).toBeInTheDocument() - const editLogo = screen.getByAltText(/black graphic of a notepad/) - expect(editLogo).toBeInTheDocument() const addUserLogo = screen.getByAltText(/black graphic of a add user button/) expect(addUserLogo).toBeInTheDocument() const binLogo = screen.getByAltText(/black graphic of a trash bin/) diff --git a/src/__tests__/NewModal.test.js b/src/__tests__/NewModal.test.js new file mode 100644 index 0000000..bb807c9 --- /dev/null +++ b/src/__tests__/NewModal.test.js @@ -0,0 +1,68 @@ +import React from "react" +import { render, fireEvent, screen } from "@testing-library/react" +import { act } from "react" +import NewModal from "../components/NewModal" + +const mockCreateNote = jest.fn() + +const user = { + id: "user123" +} + +describe("NewModal component tests", () => { + beforeEach(() => { + render() + }) + + test("should toggle the modal on button click", () => { + const openModalButton = screen.getByAltText("Edit icon") + act(() => { + fireEvent.click(openModalButton) + }) + expect(screen.getByText("New Note")).toBeInTheDocument() + }) + + test("should handle form input and submit", async () => { + act(() => { + fireEvent.click(screen.getByAltText("Edit icon")) + }) + + await act(async () => { + fireEvent.change(screen.getByLabelText("Title"), { + target: { value: "Test Title" } + }) + fireEvent.change(screen.getByLabelText("Content"), { + target: { value: "Test Content" } + }) + fireEvent.click(screen.getByLabelText("Public")) + }) + + expect(screen.getByLabelText("Title").value).toBe("Test Title") + expect(screen.getByLabelText("Content").value).toBe("Test Content") + expect(screen.getByLabelText("Public").checked).toBeTruthy() + + await act(async () => { + fireEvent.submit(screen.getByText("Create Note")) + }) + + expect(mockCreateNote).toHaveBeenCalledWith({ + title: "Test Title", + content: "Test Content", + public: true, + creator: "user123" + }) + }) + + test("should display error when submitting empty form", async () => { + act(() => { + fireEvent.click(screen.getByAltText("Edit icon")) + }) + + await act(async () => { + fireEvent.submit(screen.getByText("Create Note")) + }) + + expect(screen.getByText("Title is required")).toBeInTheDocument() + expect(screen.getByText("Content is required")).toBeInTheDocument() + }) +}) From f3af12abf49a5d674c5ade54bba68173d5b3c712 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 13 May 2024 13:22:04 -0700 Subject: [PATCH 5/6] added styles and uupdated test. --- src/__tests__/NewModal.test.js | 22 ++++++++- src/components/NewModal.js | 83 ++++++++++++++++++---------------- tailwind.config.js | 3 +- 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/__tests__/NewModal.test.js b/src/__tests__/NewModal.test.js index bb807c9..6e29430 100644 --- a/src/__tests__/NewModal.test.js +++ b/src/__tests__/NewModal.test.js @@ -4,7 +4,6 @@ import { act } from "react" import NewModal from "../components/NewModal" const mockCreateNote = jest.fn() - const user = { id: "user123" } @@ -16,10 +15,18 @@ describe("NewModal component tests", () => { test("should toggle the modal on button click", () => { const openModalButton = screen.getByAltText("Edit icon") + + expect(screen.queryByText("Create a New Note")).not.toBeInTheDocument() + act(() => { fireEvent.click(openModalButton) }) - expect(screen.getByText("New Note")).toBeInTheDocument() + expect(screen.getByText("Create a New Note")).toBeInTheDocument() + + act(() => { + fireEvent.click(screen.getByRole("button", { name: "Close" })) + }) + expect(screen.queryByText("Create a New Note")).not.toBeInTheDocument() }) test("should handle form input and submit", async () => { @@ -27,6 +34,12 @@ describe("NewModal component tests", () => { fireEvent.click(screen.getByAltText("Edit icon")) }) + await act(async () => { + fireEvent.submit(screen.getByText("Create Note")) + }) + expect(screen.getByText("Title is required")).toBeInTheDocument() + expect(screen.getByText("Content is required")).toBeInTheDocument() + await act(async () => { fireEvent.change(screen.getByLabelText("Title"), { target: { value: "Test Title" } @@ -51,6 +64,9 @@ describe("NewModal component tests", () => { public: true, creator: "user123" }) + + expect(screen.queryByText("Title is required")).not.toBeInTheDocument() + expect(screen.queryByText("Content is required")).not.toBeInTheDocument() }) test("should display error when submitting empty form", async () => { @@ -64,5 +80,7 @@ describe("NewModal component tests", () => { expect(screen.getByText("Title is required")).toBeInTheDocument() expect(screen.getByText("Content is required")).toBeInTheDocument() + + expect(screen.getByText("Create a New Note")).toBeInTheDocument() }) }) diff --git a/src/components/NewModal.js b/src/components/NewModal.js index f17f754..21bc50a 100644 --- a/src/components/NewModal.js +++ b/src/components/NewModal.js @@ -69,14 +69,17 @@ const NewModal = ({ createNote, user }) => { Edit icon dispatch({ type: "TOGGLE_MODAL" })} > - New Note + +

+ Create a New Note +

-
- Make Note Public or Private -
+
+
{
-
-
-
-
- handleInputChange("title", e.target.value)} - /> - {state.errors.title && ( -
Title is required
- )} -
-
-
-
- handleInputChange("content", e.target.value)} - /> - {state.errors.content && ( -
Content is required
- )} -
-
+ + handleInputChange("title", e.target.value)} + /> + {state.errors.title && ( +

+ Title is required +

+ )} + + +