From 355b78145e3dc99638b35b5ef4d388b7fb4dd19b Mon Sep 17 00:00:00 2001 From: Boris Rorsvort Date: Sun, 25 Jan 2026 18:13:21 +0100 Subject: [PATCH] refactor implem --- ui/src/common/useSearchRefocus.js | 41 ++++++++++++------- ui/src/common/useSearchRefocus.test.js | 55 +++++++++++++++----------- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/ui/src/common/useSearchRefocus.js b/ui/src/common/useSearchRefocus.js index 27898b969..4e8c22d2b 100644 --- a/ui/src/common/useSearchRefocus.js +++ b/ui/src/common/useSearchRefocus.js @@ -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]) } diff --git a/ui/src/common/useSearchRefocus.test.js b/ui/src/common/useSearchRefocus.test.js index 8498633fa..20f1cfb90 100644 --- a/ui/src/common/useSearchRefocus.test.js +++ b/ui/src/common/useSearchRefocus.test.js @@ -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 = ` -
- - +
+
` 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() }) })