Skip to content

Commit fb570b2

Browse files
committed
feat: add options to clear text before typing and to clear text without blurring
1 parent 8255d35 commit fb570b2

File tree

11 files changed

+1274
-7
lines changed

11 files changed

+1274
-7
lines changed

src/user-event/__tests__/__snapshots__/clear.test.tsx.snap

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,107 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

3+
exports[`clear() skips blur and endEditing events when \`skipBlur: true\`: value: "Hello!" skipBlur: true 1`] = `
4+
[
5+
{
6+
"name": "focus",
7+
"payload": {
8+
"currentTarget": {},
9+
"isDefaultPrevented": [Function],
10+
"isPersistent": [Function],
11+
"isPropagationStopped": [Function],
12+
"nativeEvent": {
13+
"target": 0,
14+
},
15+
"persist": [Function],
16+
"preventDefault": [Function],
17+
"stopPropagation": [Function],
18+
"target": {},
19+
"timeStamp": 0,
20+
},
21+
},
22+
{
23+
"name": "selectionChange",
24+
"payload": {
25+
"currentTarget": {},
26+
"isDefaultPrevented": [Function],
27+
"isPersistent": [Function],
28+
"isPropagationStopped": [Function],
29+
"nativeEvent": {
30+
"selection": {
31+
"end": 6,
32+
"start": 0,
33+
},
34+
},
35+
"persist": [Function],
36+
"preventDefault": [Function],
37+
"stopPropagation": [Function],
38+
"target": {},
39+
"timeStamp": 0,
40+
},
41+
},
42+
{
43+
"name": "keyPress",
44+
"payload": {
45+
"currentTarget": {},
46+
"isDefaultPrevented": [Function],
47+
"isPersistent": [Function],
48+
"isPropagationStopped": [Function],
49+
"nativeEvent": {
50+
"key": "Backspace",
51+
},
52+
"persist": [Function],
53+
"preventDefault": [Function],
54+
"stopPropagation": [Function],
55+
"target": {},
56+
"timeStamp": 0,
57+
},
58+
},
59+
{
60+
"name": "change",
61+
"payload": {
62+
"currentTarget": {},
63+
"isDefaultPrevented": [Function],
64+
"isPersistent": [Function],
65+
"isPropagationStopped": [Function],
66+
"nativeEvent": {
67+
"eventCount": 0,
68+
"target": 0,
69+
"text": "",
70+
},
71+
"persist": [Function],
72+
"preventDefault": [Function],
73+
"stopPropagation": [Function],
74+
"target": {},
75+
"timeStamp": 0,
76+
},
77+
},
78+
{
79+
"name": "changeText",
80+
"payload": "",
81+
},
82+
{
83+
"name": "selectionChange",
84+
"payload": {
85+
"currentTarget": {},
86+
"isDefaultPrevented": [Function],
87+
"isPersistent": [Function],
88+
"isPropagationStopped": [Function],
89+
"nativeEvent": {
90+
"selection": {
91+
"end": 0,
92+
"start": 0,
93+
},
94+
},
95+
"persist": [Function],
96+
"preventDefault": [Function],
97+
"stopPropagation": [Function],
98+
"target": {},
99+
"timeStamp": 0,
100+
},
101+
},
102+
]
103+
`;
104+
3105
exports[`clear() supports basic case: value: "Hello! 1`] = `
4106
[
5107
{

src/user-event/__tests__/clear.test.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,45 @@ describe('clear()', () => {
219219
await user.clear(input);
220220
expect(input).toHaveDisplayValue('');
221221
});
222+
223+
it('skips blur and endEditing events when `skipBlur: true`', async () => {
224+
const { textInput, events } = renderTextInputWithToolkit({
225+
value: 'Hello!',
226+
});
227+
228+
const user = userEvent.setup();
229+
await user.clear(textInput, {
230+
skipBlur: true,
231+
});
232+
233+
const eventNames = getEventsNames(events);
234+
235+
// Ensure 'endEditing' and 'blur' are not present
236+
expect(eventNames).not.toContain('endEditing');
237+
expect(eventNames).not.toContain('blur');
238+
239+
// Verify the exact events that should be present
240+
expect(eventNames).toEqual([
241+
'focus',
242+
'selectionChange',
243+
'keyPress',
244+
'change',
245+
'changeText',
246+
'selectionChange',
247+
]);
248+
249+
expect(events).toMatchSnapshot('value: "Hello!" skipBlur: true');
250+
});
251+
252+
it('sets native state value for unmanaged text inputs when `skipBlur: true`', async () => {
253+
render(<TextInput testID="input" />);
254+
255+
const user = userEvent.setup();
256+
const input = screen.getByTestId('input');
257+
await user.type(input, 'abc');
258+
expect(input).toHaveDisplayValue('abc');
259+
260+
await user.clear(input, { skipBlur: true });
261+
expect(input).toHaveDisplayValue('');
262+
});
222263
});

src/user-event/clear.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ import type { UserEventInstance } from './setup';
99
import { emitTypingEvents } from './type/type';
1010
import { dispatchEvent, wait } from './utils';
1111

12-
export async function clear(this: UserEventInstance, element: ReactTestInstance): Promise<void> {
12+
export interface BlurOptions {
13+
skipBlur?: boolean;
14+
}
15+
16+
export async function clear(
17+
this: UserEventInstance,
18+
element: ReactTestInstance,
19+
options?: BlurOptions,
20+
): Promise<void> {
1321
if (!isHostTextInput(element)) {
1422
throw new ErrorWithStack(
1523
`clear() only supports host "TextInput" elements. Passed element has type: "${element.type}".`,
@@ -45,7 +53,9 @@ export async function clear(this: UserEventInstance, element: ReactTestInstance)
4553
});
4654

4755
// 4. Exit element
48-
await wait(this.config);
49-
await dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(emptyText));
50-
await dispatchEvent(element, 'blur', EventBuilder.Common.blur());
56+
if (!options?.skipBlur) {
57+
await wait(this.config);
58+
await dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(emptyText));
59+
await dispatchEvent(element, 'blur', EventBuilder.Common.blur());
60+
}
5161
}

src/user-event/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22

3+
import type { BlurOptions } from './clear';
34
import type { PressOptions } from './press';
45
import type { ScrollToOptions } from './scroll';
56
import { setup } from './setup';
@@ -16,7 +17,7 @@ export const userEvent = {
1617
setup().longPress(element, options),
1718
type: (element: ReactTestInstance, text: string, options?: TypeOptions) =>
1819
setup().type(element, text, options),
19-
clear: (element: ReactTestInstance) => setup().clear(element),
20+
clear: (element: ReactTestInstance, options?: BlurOptions) => setup().clear(element, options),
2021
paste: (element: ReactTestInstance, text: string) => setup().paste(element, text),
2122
scrollTo: (element: ReactTestInstance, options: ScrollToOptions) =>
2223
setup().scrollTo(element, options),

src/user-event/setup/setup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ReactTestInstance } from 'react-test-renderer';
22

33
import { jestFakeTimersAreEnabled } from '../../helpers/timers';
44
import { wrapAsync } from '../../helpers/wrap-async';
5+
import type { BlurOptions } from '../clear';
56
import { clear } from '../clear';
67
import { paste } from '../paste';
78
import type { PressOptions } from '../press';
@@ -122,7 +123,7 @@ export interface UserEventInstance {
122123
*
123124
* @param element TextInput element to clear
124125
*/
125-
clear: (element: ReactTestInstance) => Promise<void>;
126+
clear: (element: ReactTestInstance, options?: BlurOptions) => Promise<void>;
126127

127128
/**
128129
* Simulate user pasting the text to a given `TextInput` element.

0 commit comments

Comments
 (0)