Skip to content

Commit bb43781

Browse files
[Fabric] Implement the onPressIn property for the fabric implementation of TextInput (#14480)
* Implemented OnPressIn event for textinput ---------
1 parent 02050f1 commit bb43781

File tree

11 files changed

+209
-5
lines changed

11 files changed

+209
-5
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "Implemented OnPressIn event for textinput",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "none"
7+
}

packages/@react-native-windows/tester/src/js/examples/TextInput/TextInputExample.windows.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,17 @@ class PressInOutEvents extends React.Component<
8080
super(props);
8181
this.state = {text: 'PressIn/PressOut message'};
8282
}
83+
8384
render() {
8485
return (
8586
<View>
86-
<Text>{this.state.text}</Text>
87+
<Text testID="textinput-state-display">{this.state.text}</Text>
8788
<ExampleTextInput
8889
placeholder="Click inside the box to observe events being fired."
8990
style={[styles.singleLineWithHeightTextInput]}
90-
onPressIn={() =>
91-
this.setState({text: 'Holding down the click/touch'})
92-
}
91+
onPressIn={() => {
92+
this.setState({text: 'Holding down the click/touch'});
93+
}}
9394
onPressOut={() => this.setState({text: 'Released click/touch'})}
9495
testID="textinput-press"
9596
/>

packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ afterEach(async () => {
2121
await verifyNoErrorLogs();
2222
});
2323

24+
const searchBox = async (input: string) => {
25+
const searchBox = await app.findElementByTestID('example_search');
26+
await app.waitUntil(
27+
async () => {
28+
await searchBox.setValue(input);
29+
return (await searchBox.getText()) === input;
30+
},
31+
{
32+
interval: 1500,
33+
timeout: 5000,
34+
timeoutMsg: `Unable to enter correct search text into test searchbox.`,
35+
},
36+
);
37+
};
38+
2439
describe('TextInput Tests', () => {
2540
test('TextInputs can rewrite characters: Replace Space with Underscore', async () => {
2641
const component = await app.findElementByTestID(
@@ -179,6 +194,34 @@ describe('TextInput Tests', () => {
179194
},
180195
);
181196
});
197+
test('TextInput triggers onPressIn and updates state text', async () => {
198+
// Scroll the example into view
199+
await searchBox('onPressIn');
200+
const component = await app.findElementByTestID('textinput-press');
201+
await component.waitForDisplayed({timeout: 5000});
202+
const dump = await dumpVisualTree('textinput-press');
203+
expect(dump).toMatchSnapshot();
204+
205+
// Trigger onPressIn (click only)
206+
await component.click();
207+
const stateText = await app.findElementByTestID('textinput-state-display');
208+
209+
await app.waitUntil(
210+
async () => {
211+
const currentText = await stateText.getText();
212+
return currentText === 'Holding down the click/touch';
213+
},
214+
{
215+
timeout: 5000,
216+
timeoutMsg: 'State text not updated after onPressIn.',
217+
},
218+
);
219+
// Assertion
220+
expect(await stateText.getText()).toBe('Holding down the click/touch');
221+
// This step helps avoid UI lock by unfocusing the input
222+
const search = await app.findElementByTestID('example_search');
223+
await search.setValue('');
224+
});
182225
test('TextInputs can have attributed text', async () => {
183226
const component = await app.findElementByTestID('text-input');
184227
await component.waitForDisplayed({timeout: 5000});

packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,84 @@ exports[`TextInput Tests Text have cursorColor 1`] = `
391391
}
392392
`;
393393

394+
exports[`TextInput Tests TextInput triggers onPressIn and updates state text 1`] = `
395+
{
396+
"Automation Tree": {
397+
"AutomationId": "textinput-press",
398+
"ControlType": 50004,
399+
"HelpText": "Click inside the box to observe events being fired.",
400+
"IsKeyboardFocusable": true,
401+
"LocalizedControlType": "edit",
402+
"Name": "Click inside the box to observe events being fired.",
403+
"TextRangePattern.GetText": "Click inside the box to observe events being fired.",
404+
},
405+
"Component Tree": {
406+
"Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView",
407+
"_Props": {
408+
"TestId": "textinput-press",
409+
},
410+
},
411+
"Visual Tree": {
412+
"Comment": "textinput-press",
413+
"Offset": "0, 0, 0",
414+
"Size": "916, 30",
415+
"Visual Type": "SpriteVisual",
416+
"__Children": [
417+
{
418+
"Offset": "0, 0, 0",
419+
"Size": "1, 1",
420+
"Visual Type": "SpriteVisual",
421+
},
422+
{
423+
"Offset": "1, 0, 0",
424+
"Size": "-2, 1",
425+
"Visual Type": "SpriteVisual",
426+
},
427+
{
428+
"Offset": "-1, 0, 0",
429+
"Size": "1, 1",
430+
"Visual Type": "SpriteVisual",
431+
},
432+
{
433+
"Offset": "-1, 1, 0",
434+
"Size": "1, -2",
435+
"Visual Type": "SpriteVisual",
436+
},
437+
{
438+
"Offset": "-1, -1, 0",
439+
"Size": "1, 1",
440+
"Visual Type": "SpriteVisual",
441+
},
442+
{
443+
"Offset": "1, -1, 0",
444+
"Size": "-2, 1",
445+
"Visual Type": "SpriteVisual",
446+
},
447+
{
448+
"Offset": "0, -1, 0",
449+
"Size": "1, 1",
450+
"Visual Type": "SpriteVisual",
451+
},
452+
{
453+
"Offset": "0, 1, 0",
454+
"Size": "1, -2",
455+
"Visual Type": "SpriteVisual",
456+
},
457+
{
458+
"Brush": {
459+
"Brush Type": "ColorBrush",
460+
"Color": "rgba(0, 0, 0, 255)",
461+
},
462+
"Offset": "0, 0, 0",
463+
"Opacity": 0,
464+
"Size": "0, 0",
465+
"Visual Type": "SpriteVisual",
466+
},
467+
],
468+
},
469+
}
470+
`;
471+
394472
exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Characters 1`] = `
395473
{
396474
"Automation Tree": {

packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79941,7 +79941,9 @@ exports[`snapshotAllPages TextInput 37`] = `
7994179941

7994279942
exports[`snapshotAllPages TextInput 38`] = `
7994379943
<View>
79944-
<Text>
79944+
<Text
79945+
testID="textinput-state-display"
79946+
>
7994579947
PressIn/PressOut message
7994679948
</Text>
7994779949
<TextInput

packages/playground/Samples/textinput.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
KeyboardAvoidingView,
1616
ScrollView,
1717
TouchableWithoutFeedback,
18+
Alert,
1819
} from 'react-native';
1920

2021
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
@@ -219,6 +220,13 @@ export default class Bootstrap extends React.Component<{}, any> {
219220
{backgroundColor: 'black', color: 'white', marginBottom: 4},
220221
]}
221222
/>
223+
<TextInput
224+
style={styles.input}
225+
placeholder="OnPressIn..."
226+
onPressIn={event => {
227+
Alert.alert('Pressed!');
228+
}}
229+
/>
222230
<TextInput
223231
placeholder="Single line with selection color"
224232
cursorColor="#00FF00"

vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,24 @@ void WindowsTextInputComponentView::OnPointerPressed(
683683
auto hr = m_textServices->TxSendMessage(msg, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam), &lresult);
684684
args.Handled(hr != S_FALSE);
685685
}
686+
687+
// Emits the OnPressIn event
688+
if (m_eventEmitter && !m_comingFromJS) {
689+
auto emitter = std::static_pointer_cast<const facebook::react::WindowsTextInputEventEmitter>(m_eventEmitter);
690+
float offsetX = position.X - m_layoutMetrics.frame.origin.x;
691+
float offsetY = position.Y - m_layoutMetrics.frame.origin.y;
692+
float neutralX = m_layoutMetrics.frame.origin.x;
693+
float neutralY = m_layoutMetrics.frame.origin.y;
694+
695+
facebook::react::PressEvent pressInArgs;
696+
pressInArgs.target = m_tag;
697+
pressInArgs.pagePoint = {position.X, position.Y};
698+
pressInArgs.offsetPoint = {offsetX, offsetY}; //{LocationX,LocationY}
699+
pressInArgs.timestamp = static_cast<double>(pp.Timestamp()) / 1000.0;
700+
pressInArgs.identifier = pp.PointerId();
701+
702+
emitter->onPressIn(pressInArgs);
703+
}
686704
}
687705

688706
void WindowsTextInputComponentView::OnPointerReleased(

vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ void WindowsTextInputEventEmitter::onContentSizeChange(OnContentSizeChange event
6767
});
6868
}
6969

70+
void WindowsTextInputEventEmitter::onPressIn(PressEvent event) const {
71+
dispatchEvent("textInputPressIn", [event = std::move(event)](jsi::Runtime &runtime) {
72+
auto payload = jsi::Object(runtime);
73+
auto nativeEvent = jsi::Object(runtime);
74+
nativeEvent.setProperty(runtime, "target", static_cast<double>(event.target));
75+
nativeEvent.setProperty(runtime, "pageX", event.pagePoint.x);
76+
nativeEvent.setProperty(runtime, "pageY", event.pagePoint.y);
77+
nativeEvent.setProperty(runtime, "locationX", event.offsetPoint.x);
78+
nativeEvent.setProperty(runtime, "locationY", event.offsetPoint.y);
79+
nativeEvent.setProperty(runtime, "timestamp", event.timestamp);
80+
nativeEvent.setProperty(runtime, "identifier", static_cast<double>(event.identifier));
81+
payload.setProperty(runtime, "nativeEvent", nativeEvent);
82+
return payload;
83+
});
84+
}
85+
7086
void WindowsTextInputEventEmitter::onEndEditing(OnEndEditing event) const {
7187
dispatchEvent("textInputEndEditing", [event = std::move(event)](jsi::Runtime &runtime) {
7288
auto payload = jsi::Object(runtime);

vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class WindowsTextInputEventEmitter : public ViewEventEmitter {
5252
void onSubmitEditing(OnSubmitEditing value) const;
5353
void onKeyPress(OnKeyPress value) const;
5454
void onContentSizeChange(OnContentSizeChange value) const;
55+
void onPressIn(PressEvent event) const override;
5556
void onEndEditing(OnEndEditing value) const;
5657
};
5758

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewEventEmitter.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,22 @@ void HostPlatformViewEventEmitter::onMouseLeave(PointerEvent const &pointerEvent
4646
dispatchEvent("mouseLeave", std::make_shared<PointerEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
4747
}
4848

49+
#pragma mark - Touch Events
50+
51+
void HostPlatformViewEventEmitter::onPressIn(PressEvent event) const {
52+
dispatchEvent("pressIn", [event](jsi::Runtime &runtime) {
53+
auto payload = jsi::Object(runtime);
54+
auto nativeEvent = jsi::Object(runtime);
55+
nativeEvent.setProperty(runtime, "target", static_cast<double>(event.target));
56+
nativeEvent.setProperty(runtime, "pageX", event.pagePoint.x);
57+
nativeEvent.setProperty(runtime, "pageY", event.pagePoint.y);
58+
nativeEvent.setProperty(runtime, "locationX", event.offsetPoint.x);
59+
nativeEvent.setProperty(runtime, "locationY", event.offsetPoint.y);
60+
nativeEvent.setProperty(runtime, "timestamp", event.timestamp);
61+
nativeEvent.setProperty(runtime, "identifier", event.identifier);
62+
payload.setProperty(runtime, "nativeEvent", nativeEvent);
63+
return payload;
64+
});
65+
}
66+
4967
} // namespace facebook::react

0 commit comments

Comments
 (0)