Skip to content

Commit 3c02025

Browse files
authored
fix: trim whitespace on Search filter fields to prevent failed searches (#3811)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 3ae6577 commit 3c02025

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

src/components/Search/Search.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ export const Search = ({
2626
inputRef,
2727
...props
2828
}: SearchProps) => {
29+
const onUpdate = React.useCallback(
30+
(newValue: string) => {
31+
onChange(newValue.trim());
32+
},
33+
[onChange],
34+
);
35+
2936
return (
3037
<DebouncedTextInput
3138
debounce={debounce}
@@ -35,7 +42,7 @@ export const Search = ({
3542
style={{width}}
3643
className={b(null, className)}
3744
value={value}
38-
onUpdate={onChange}
45+
onUpdate={onUpdate}
3946
{...props}
4047
/>
4148
);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {render, screen} from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
4+
import {Search} from '../Search';
5+
6+
describe('Search', () => {
7+
beforeEach(() => {
8+
jest.useFakeTimers();
9+
});
10+
11+
afterEach(() => {
12+
jest.useRealTimers();
13+
});
14+
15+
it('trims leading and trailing whitespace from the onChange value', async () => {
16+
const onChange = jest.fn();
17+
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
18+
19+
render(<Search onChange={onChange} value="" />);
20+
21+
const input = screen.getByRole('textbox');
22+
await user.type(input, ' hello ');
23+
24+
jest.runAllTimers();
25+
26+
const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1];
27+
expect(lastCall[0]).toBe('hello');
28+
});
29+
30+
it('preserves internal spaces in the onChange value', async () => {
31+
const onChange = jest.fn();
32+
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
33+
34+
render(<Search onChange={onChange} value="" />);
35+
36+
const input = screen.getByRole('textbox');
37+
await user.type(input, 'hello world');
38+
39+
jest.runAllTimers();
40+
41+
const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1];
42+
expect(lastCall[0]).toBe('hello world');
43+
});
44+
45+
it('calls onChange with empty string for whitespace-only input', async () => {
46+
const onChange = jest.fn();
47+
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
48+
49+
render(<Search onChange={onChange} value="" />);
50+
51+
const input = screen.getByRole('textbox');
52+
await user.type(input, ' ');
53+
54+
jest.runAllTimers();
55+
56+
const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1];
57+
expect(lastCall[0]).toBe('');
58+
});
59+
});

0 commit comments

Comments
 (0)