refactor implem

This commit is contained in:
Boris Rorsvort 2026-01-25 18:13:21 +01:00
parent 8c2ae728af
commit 355b78145e
2 changed files with 60 additions and 36 deletions

View File

@ -1,19 +1,32 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { useLocation } from 'react-router-dom'
export const useSearchRefocus = () => {
const location = useLocation()
const prevSearchValue = useRef(null)
useEffect(() => {
const handleClick = (e) => {
const clearButton = e.target.closest('[aria-label*="clear" i]')
if (clearButton) {
setTimeout(() => {
const searchInput = document.querySelector('[class*="RaSearchInput"] input')
if (searchInput) {
searchInput.focus()
}
}, 800)
}
const params = new URLSearchParams(location.search)
const filterStr = params.get('filter') || '{}'
let filter = {}
try {
filter = JSON.parse(filterStr)
} catch (e) {
// Invalid JSON, ignore
}
document.addEventListener('click', handleClick, true)
return () => document.removeEventListener('click', handleClick, true)
}, [])
const searchValue = filter.name || filter.title || filter.q || ''
if (prevSearchValue.current && !searchValue) {
setTimeout(() => {
const input = document.querySelector('[class*="RaSearchInput"] input')
if (input) {
input.focus()
}
}, 100)
}
prevSearchValue.current = searchValue
}, [location.search])
}

View File

@ -2,6 +2,11 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
import { renderHook } from '@testing-library/react-hooks'
import { useSearchRefocus } from './useSearchRefocus'
const mockLocation = { search: '' }
vi.mock('react-router-dom', () => ({
useLocation: () => mockLocation,
}))
describe('useSearchRefocus', () => {
let container
@ -9,12 +14,12 @@ describe('useSearchRefocus', () => {
vi.useFakeTimers()
container = document.createElement('div')
container.innerHTML = `
<div class="MuiFormControl-root">
<input type="text" value="search term" />
<button aria-label="clear search">X</button>
<div class="RaSearchInput-input">
<input type="text" />
</div>
`
document.body.appendChild(container)
mockLocation.search = ''
})
afterEach(() => {
@ -22,44 +27,50 @@ describe('useSearchRefocus', () => {
document.body.removeChild(container)
})
it('focuses the input after clicking clear button', () => {
renderHook(() => useSearchRefocus())
const clearButton = container.querySelector('[aria-label="clear search"]')
it('focuses the input when search filter is cleared', () => {
const input = container.querySelector('input')
const focusSpy = vi.spyOn(input, 'focus')
clearButton.click()
mockLocation.search = '?filter={"name":"test"}'
const { rerender } = renderHook(() => useSearchRefocus())
expect(focusSpy).not.toHaveBeenCalled()
vi.advanceTimersByTime(600)
mockLocation.search = '?filter={}'
rerender()
vi.advanceTimersByTime(100)
expect(focusSpy).toHaveBeenCalledTimes(1)
})
it('does not focus if click is not on a clear button', () => {
renderHook(() => useSearchRefocus())
it('does not focus if filter was already empty', () => {
const input = container.querySelector('input')
const focusSpy = vi.spyOn(input, 'focus')
input.click()
mockLocation.search = '?filter={}'
const { rerender } = renderHook(() => useSearchRefocus())
vi.advanceTimersByTime(600)
mockLocation.search = '?filter={}'
rerender()
vi.advanceTimersByTime(100)
expect(focusSpy).not.toHaveBeenCalled()
})
it('cleans up event listener on unmount', () => {
const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener')
it('does not focus if filter value changed but not cleared', () => {
const input = container.querySelector('input')
const focusSpy = vi.spyOn(input, 'focus')
const { unmount } = renderHook(() => useSearchRefocus())
unmount()
mockLocation.search = '?filter={"name":"test"}'
const { rerender } = renderHook(() => useSearchRefocus())
expect(removeEventListenerSpy).toHaveBeenCalledWith(
'click',
expect.any(Function),
)
mockLocation.search = '?filter={"name":"other"}'
rerender()
vi.advanceTimersByTime(100)
expect(focusSpy).not.toHaveBeenCalled()
})
})