Skip to content

Commit

Permalink
implement debounced search change handling
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjgoss committed Dec 2, 2023
1 parent c694f37 commit b98596d
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 5 deletions.
14 changes: 13 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"rehype-raw": "5.1.0",
"rehype-sanitize": "4.0.0",
"remark-breaks": "2.0.2",
"remark-gfm": "1.0.0"
"remark-gfm": "1.0.0",
"use-debounce": "^10.0.0"
},
"peerDependencies": {
"react": "~18",
Expand Down
21 changes: 21 additions & 0 deletions src/components/app/SearchBar.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ describe('SearchBar', () => {
expect(getByRole('textbox', { name: 'Search' })).to.have.value('keyword')
})

it('fires an event after half a second when the user types a query', async () => {
const onChange = sinon.fake()
const { getByRole } = render(
<SearchBar
query={''}
onSearch={onChange}
hideStatuses={[]}
statusesWithScenarios={[]}
onFilter={sinon.fake()}
/>
)

await userEvent.type(getByRole('textbox', { name: 'Search' }), 'search text')

expect(onChange).not.to.have.been.called

await new Promise((resolve) => setTimeout(resolve, 500))

expect(onChange).to.have.been.called
})

it('fires an event with the query when the form is submitted', async () => {
const onChange = sinon.fake()
const { getByRole } = render(
Expand Down
9 changes: 6 additions & 3 deletions src/components/app/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TestStepResultStatus as Status } from '@cucumber/messages'
import { faFilter, faSearch } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import React, { FunctionComponent } from 'react'
import { useDebouncedCallback } from 'use-debounce'

import statusName from '../gherkin/statusName.js'
import styles from './SearchBar.module.scss'
Expand All @@ -22,11 +23,12 @@ export const SearchBar: FunctionComponent<IProps> = ({
onSearch,
onFilter,
}) => {
const debouncedSearchChange = useDebouncedCallback((newValue) => {
onSearch(newValue)
}, 500)
const searchSubmitted = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
const formData = new window.FormData(event.currentTarget)
const query = formData.get('query')
onSearch((query || '').toString())
debouncedSearchChange.flush()
}
const filterChanged = (name: Status, show: boolean) => {
onFilter(show ? hideStatuses.filter((s) => s !== name) : hideStatuses.concat(name))
Expand All @@ -42,6 +44,7 @@ export const SearchBar: FunctionComponent<IProps> = ({
name="query"
placeholder="Search with text or @tags"
defaultValue={query}
onChange={(e) => debouncedSearchChange(e.target.value)}
/>
<small className={styles.searchHelp}>
You can search with plain text or{' '}
Expand Down

0 comments on commit b98596d

Please sign in to comment.