From 609a9870e0a6e77849beced6689940c30209b8f4 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 23 Oct 2023 14:53:30 -0400 Subject: [PATCH 1/6] make searchbar controlled component --- src/components/SearchForm/SearchForm.tsx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx index a85c448c9..bf8ad5b3e 100644 --- a/src/components/SearchForm/SearchForm.tsx +++ b/src/components/SearchForm/SearchForm.tsx @@ -1,11 +1,11 @@ import { SearchBar } from "@nypl/design-system-react-components" import { useRouter } from "next/router" -import type { SyntheticEvent } from "react" +import type { SyntheticEvent, Dispatch, SetStateAction } from "react" +import { useState } from "react" import styles from "../../../styles/components/Search.module.scss" import RCLink from "../RCLink/RCLink" import { getQueryString } from "../../utils/searchUtils" -import type { SearchFormEvent } from "../../types/searchTypes" import { BASE_URL, PATHS } from "../../config/constants" /** @@ -14,19 +14,28 @@ import { BASE_URL, PATHS } from "../../config/constants" */ const SearchForm = () => { const router = useRouter() + const [searchTerm, setSearchTerm] = useState((router.query.q as string) || "") + const [searchScope, setSearchScope] = useState("all") const handleSubmit = async (e: SyntheticEvent) => { e.preventDefault() - const target = e.target as typeof e.target & SearchFormEvent const searchParams = { - q: target.q.value, - field: target.search_scope.value, + q: searchTerm, + field: searchScope, } const queryString = getQueryString(searchParams) await router.push(`${PATHS.SEARCH}${queryString}`) } + const handleInputChange = ( + e: SyntheticEvent, + setValue: Dispatch> + ) => { + const target = e.target as HTMLInputElement + setValue(target.value) + } + return (
@@ -38,6 +47,8 @@ const SearchForm = () => { onSubmit={handleSubmit} labelText="Search Bar Label" selectProps={{ + value: searchScope, + onChange: (e) => handleInputChange(e, setSearchScope), labelText: "Select a category", name: "search_scope", optionsData: [ @@ -50,6 +61,8 @@ const SearchForm = () => { ], }} textInputProps={{ + onChange: (e) => handleInputChange(e, setSearchTerm), + value: searchTerm, labelText: "Search by keyword, title, journal title, or author/contributor", name: "q", From 94f145ae1990e58e8806676977a399da33fa9eb2 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 23 Oct 2023 14:58:43 -0400 Subject: [PATCH 2/6] make dynamic search results heading --- pages/search/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pages/search/index.tsx b/pages/search/index.tsx index 5bfacb998..0d796d375 100644 --- a/pages/search/index.tsx +++ b/pages/search/index.tsx @@ -61,7 +61,9 @@ export default function Search({ results }) { {totalResults ? ( <> - {`Displaying 1-50 of ${totalResults.toLocaleString()} results for keyword "${ + {`Displaying ${ + totalResults > 50 ? "1-50" : totalResults.toLocaleString() + } of ${totalResults.toLocaleString()} results for keyword "${ searchParams.q }"`} From 992308e8e4e5214e5cf815e6997ad18a45a7ca25 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 23 Oct 2023 15:29:50 -0400 Subject: [PATCH 3/6] start tests --- src/components/SearchForm/SearchForm.test.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/components/SearchForm/SearchForm.test.tsx diff --git a/src/components/SearchForm/SearchForm.test.tsx b/src/components/SearchForm/SearchForm.test.tsx new file mode 100644 index 000000000..b6a3dcc4f --- /dev/null +++ b/src/components/SearchForm/SearchForm.test.tsx @@ -0,0 +1,25 @@ +import React from "react" +import { render, screen, fireEvent, act } from "@testing-library/react" +import mockRouter from "next-router-mock" + +import SearchForm from "./SearchForm" +import userEvent from "@testing-library/user-event" + +jest.mock("next/router", () => jest.requireActual("next-router-mock")) + +describe("SubNav", () => { + const submit = () => + fireEvent( + screen.getByRole("button", { name: "Search" }), + new MouseEvent("click") + ) + it("submits a keyword query", async () => { + render() + const input = screen.getByRole("textbox") + await act(async () => { + await userEvent.type(input, "spaghetti") + submit() + expect(mockRouter.asPath).toBe("/search?q=spaghetti") + }) + }) +}) From 4ef11562b66b5e4d216e7723fe4b5bc06505f268 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Tue, 24 Oct 2023 09:25:27 -0400 Subject: [PATCH 4/6] add more tests --- src/components/SearchForm/SearchForm.test.tsx | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/SearchForm/SearchForm.test.tsx b/src/components/SearchForm/SearchForm.test.tsx index b6a3dcc4f..c24761230 100644 --- a/src/components/SearchForm/SearchForm.test.tsx +++ b/src/components/SearchForm/SearchForm.test.tsx @@ -7,13 +7,17 @@ import userEvent from "@testing-library/user-event" jest.mock("next/router", () => jest.requireActual("next-router-mock")) -describe("SubNav", () => { +describe("SearchForm", () => { const submit = () => fireEvent( screen.getByRole("button", { name: "Search" }), new MouseEvent("click") ) - it("submits a keyword query", async () => { + afterEach(async () => { + const input = screen.getByRole("textbox") + await userEvent.clear(input) + }) + it("submits a keyword query by default", async () => { render() const input = screen.getByRole("textbox") await act(async () => { @@ -22,4 +26,23 @@ describe("SubNav", () => { expect(mockRouter.asPath).toBe("/search?q=spaghetti") }) }) + it("submits a journal_title query", async () => { + render() + const input = screen.getByRole("textbox") + const searchScopeSelect = screen.getByRole("combobox") + await act(async () => { + await userEvent.type(input, "spaghetti") + await userEvent.selectOptions(searchScopeSelect, "journal_title") + submit() + expect(mockRouter.asPath).toBe( + "/search?q=spaghettispaghetti&search_scope=journal_title" + ) + }) + }) + it("gets keyword from url", () => { + mockRouter.query.q = "spaghetti" + render() + const input = screen.getByDisplayValue("spaghetti") + expect(input).toBeTruthy() + }) }) From 8459034e2ca56fd5d6d011d1597e8d6c472e6675 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Tue, 24 Oct 2023 09:59:23 -0400 Subject: [PATCH 5/6] rename method and fix tests --- src/components/SearchForm/SearchForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx index bf8ad5b3e..e021e7f85 100644 --- a/src/components/SearchForm/SearchForm.tsx +++ b/src/components/SearchForm/SearchForm.tsx @@ -28,7 +28,7 @@ const SearchForm = () => { await router.push(`${PATHS.SEARCH}${queryString}`) } - const handleInputChange = ( + const handleChange = ( e: SyntheticEvent, setValue: Dispatch> ) => { @@ -48,7 +48,7 @@ const SearchForm = () => { labelText="Search Bar Label" selectProps={{ value: searchScope, - onChange: (e) => handleInputChange(e, setSearchScope), + onChange: (e) => handleChange(e, setSearchScope), labelText: "Select a category", name: "search_scope", optionsData: [ @@ -61,7 +61,7 @@ const SearchForm = () => { ], }} textInputProps={{ - onChange: (e) => handleInputChange(e, setSearchTerm), + onChange: (e) => handleChange(e, setSearchTerm), value: searchTerm, labelText: "Search by keyword, title, journal title, or author/contributor", From 88cf91223e8b54ad952c9bccd672d52bd021b3b1 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Tue, 24 Oct 2023 10:01:43 -0400 Subject: [PATCH 6/6] fix tests --- src/components/SearchForm/SearchForm.test.tsx | 5 ++++- src/components/SearchForm/SearchForm.tsx | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/SearchForm/SearchForm.test.tsx b/src/components/SearchForm/SearchForm.test.tsx index c24761230..d8c194506 100644 --- a/src/components/SearchForm/SearchForm.test.tsx +++ b/src/components/SearchForm/SearchForm.test.tsx @@ -13,6 +13,9 @@ describe("SearchForm", () => { screen.getByRole("button", { name: "Search" }), new MouseEvent("click") ) + beforeEach(() => { + mockRouter.query.q = "" + }) afterEach(async () => { const input = screen.getByRole("textbox") await userEvent.clear(input) @@ -35,7 +38,7 @@ describe("SearchForm", () => { await userEvent.selectOptions(searchScopeSelect, "journal_title") submit() expect(mockRouter.asPath).toBe( - "/search?q=spaghettispaghetti&search_scope=journal_title" + "/search?q=spaghetti&search_scope=journal_title" ) }) }) diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx index e021e7f85..585e5e5e1 100644 --- a/src/components/SearchForm/SearchForm.tsx +++ b/src/components/SearchForm/SearchForm.tsx @@ -14,7 +14,9 @@ import { BASE_URL, PATHS } from "../../config/constants" */ const SearchForm = () => { const router = useRouter() - const [searchTerm, setSearchTerm] = useState((router.query.q as string) || "") + const [searchTerm, setSearchTerm] = useState( + (router?.query?.q as string) || "" + ) const [searchScope, setSearchScope] = useState("all") const handleSubmit = async (e: SyntheticEvent) => {