add debounced search on type to the search bar (#2703)

* debounced search on type

* loading workspaces on page entry

* fixing e2e test

* removing boilerplate
This commit is contained in:
Kira Pilot
2022-06-29 13:43:41 -04:00
committed by GitHub
parent 6a55889362
commit d9668f7a4e
7 changed files with 78 additions and 11 deletions
@@ -0,0 +1,38 @@
import { fireEvent, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { render } from "../../testHelpers/renderHelpers"
import { SearchBarWithFilter } from "./SearchBarWithFilter"
// mock the debounce utility
jest.mock("just-debounce-it", () =>
jest.fn((fn) => {
fn.cancel = jest.fn()
return fn
}),
)
describe("SearchBarWithFilter", () => {
it("calls the onFilter handler on keystroke", async () => {
// When
const onFilter = jest.fn()
render(<SearchBarWithFilter onFilter={onFilter} />)
const searchInput = screen.getByRole("textbox")
await userEvent.type(searchInput, "workspace") // 9 characters
// Then
expect(onFilter).toBeCalledTimes(10) // 9 characters + 1 on component mount
})
it("calls the onFilter handler on submit", async () => {
// When
const onFilter = jest.fn()
render(<SearchBarWithFilter onFilter={onFilter} />)
const searchInput = screen.getByRole("textbox")
await fireEvent.keyDown(searchInput, { key: "Enter", code: "Enter", charCode: 13 })
// Then
expect(onFilter).toBeCalledTimes(1)
})
})
@@ -7,7 +7,8 @@ import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import SearchIcon from "@material-ui/icons/Search"
import { FormikErrors, useFormik } from "formik"
import { useState } from "react"
import debounce from "just-debounce-it"
import { useCallback, useEffect, useState } from "react"
import { getValidationErrorMessage } from "../../api/errors"
import { getFormHelpers } from "../../util/formUtils"
import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows"
@@ -53,6 +54,23 @@ export const SearchBarWithFilter: React.FC<SearchBarWithFilterProps> = ({
},
})
// debounce query string entry by user
// we want the dependency array empty here
// as we don't need to redefine the function
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedOnFilter = useCallback(
debounce((debouncedQueryString: string) => {
onFilter(debouncedQueryString)
}, 300),
[],
)
// update the query params while typing
useEffect(() => {
debouncedOnFilter(form.values.query)
return () => debouncedOnFilter.cancel()
}, [debouncedOnFilter, form.values.query])
const getFieldHelpers = getFormHelpers<FilterFormValues>(form)
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
@@ -12,6 +12,7 @@ const WorkspacesPage: FC = () => {
const [searchParams, setSearchParams] = useSearchParams()
const { workspaceRefs } = workspacesState.context
// On page load, populate the table with workspaces
useEffect(() => {
const filter = searchParams.get("filter")
const query = filter ?? workspaceFilterQuery.me
@@ -20,7 +21,8 @@ const WorkspacesPage: FC = () => {
type: "GET_WORKSPACES",
query,
})
}, [searchParams, send])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<>
@@ -33,8 +35,11 @@ const WorkspacesPage: FC = () => {
loading={workspacesState.hasTag("loading")}
workspaceRefs={workspaceRefs}
onFilter={(query) => {
searchParams.set("filter", query)
setSearchParams(searchParams)
setSearchParams({ filter: query })
send({
type: "GET_WORKSPACES",
query,
})
}}
/>
</>