Skip to content

Commit c390c66

Browse files
authored
[Fabric] Add onSubmitEditing and clearTextOnSubmit (#12746)
* sumbitkeyevents working with shift * Change files * use keyState * add functional test * update snapshot
1 parent c2b1a3c commit c390c66

File tree

8 files changed

+151
-10
lines changed

8 files changed

+151
-10
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "sumbitkeyevents working with shift",
4+
"packageName": "react-native-windows",
5+
"email": "tatianakapos@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,28 @@ describe('TextInput Tests', () => {
264264
'textinput-clear-on-submit',
265265
);
266266
await component.waitForDisplayed({timeout: 5000});
267+
await app.waitUntil(
268+
async () => {
269+
await component.setValue('Hello World');
270+
return (await component.getText()) === 'Hello World';
271+
},
272+
{
273+
interval: 1500,
274+
timeout: 5000,
275+
timeoutMsg: `Unable to enter correct text.`,
276+
},
277+
);
278+
await app.waitUntil(
279+
async () => {
280+
await component.setValue('\uE007');
281+
return (await component.getText()) === '';
282+
},
283+
{
284+
interval: 1500,
285+
timeout: 5000,
286+
timeoutMsg: `Unable to enter correct text.`,
287+
},
288+
);
267289
const dump = await dumpVisualTree('textinput-clear-on-submit');
268290
expect(dump).toMatchSnapshot();
269291
});
@@ -283,11 +305,33 @@ describe('TextInput Tests', () => {
283305
const dump = await dumpVisualTree('textinput-clear-on-submit-3');
284306
expect(dump).toMatchSnapshot();
285307
});
286-
test('TextInputs can submit with custom key', async () => {
308+
test('TextInputs can submit with custom key, multilined and submit with enter', async () => {
287309
const component = await app.findElementByTestID(
288310
'textinput-clear-on-submit-4',
289311
);
290312
await component.waitForDisplayed({timeout: 5000});
313+
await app.waitUntil(
314+
async () => {
315+
await component.setValue('Hello World');
316+
return (await component.getText()) === 'Hello World';
317+
},
318+
{
319+
interval: 1500,
320+
timeout: 5000,
321+
timeoutMsg: `Unable to enter correct text.`,
322+
},
323+
);
324+
await app.waitUntil(
325+
async () => {
326+
await component.setValue('\uE007');
327+
return (await component.getText()) === '';
328+
},
329+
{
330+
interval: 1500,
331+
timeout: 5000,
332+
timeoutMsg: `Unable to enter correct text.`,
333+
},
334+
);
291335
const dump = await dumpVisualTree('textinput-clear-on-submit-4');
292336
expect(dump).toMatchSnapshot();
293337
});

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,8 +2421,8 @@ exports[`TextInput Tests TextInputs can clear on submit 1`] = `
24212421
],
24222422
"Opacity": 0,
24232423
"Size": [
2424-
0,
2425-
0,
2424+
1,
2425+
19,
24262426
],
24272427
"Visual Type": "SpriteVisual",
24282428
},
@@ -6071,7 +6071,7 @@ exports[`TextInput Tests TextInputs can set their readOnly prop to true 1`] = `
60716071
}
60726072
`;
60736073

6074-
exports[`TextInput Tests TextInputs can submit with custom key 1`] = `
6074+
exports[`TextInput Tests TextInputs can submit with custom key, multilined and submit with enter 1`] = `
60756075
{
60766076
"Automation Tree": {
60776077
"AutomationId": "textinput-clear-on-submit-4",
@@ -6096,8 +6096,8 @@ exports[`TextInput Tests TextInputs can submit with custom key 1`] = `
60966096
],
60976097
"Opacity": 0,
60986098
"Size": [
6099-
0,
6100-
0,
6099+
1,
6100+
19,
61016101
],
61026102
"Visual Type": "SpriteVisual",
61036103
},

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,44 @@ void WindowsTextInputComponentView::OnKeyUp(
798798
Super::OnKeyDown(source, args);
799799
}
800800

801+
bool WindowsTextInputComponentView::ShouldSubmit(
802+
const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source,
803+
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
804+
bool shouldSubmit = true;
805+
806+
if (shouldSubmit) {
807+
if (!m_multiline && m_submitKeyEvents.size() == 0) {
808+
// If no 'submitKeyEvents' are supplied, use the default behavior for single-line TextInput
809+
shouldSubmit = args.KeyCode() == '\r';
810+
} else if (m_submitKeyEvents.size() > 0) {
811+
auto submitKeyEvent = m_submitKeyEvents.at(0);
812+
// If 'submitKeyEvents' are supplied, use them to determine whether to emit onSubmitEditing' for either
813+
// single-line or multi-line TextInput
814+
if (args.KeyCode() == '\r') {
815+
bool shiftDown = source.GetKeyState(winrt::Windows::System::VirtualKey::Shift) ==
816+
winrt::Windows::UI::Core::CoreVirtualKeyStates::Down;
817+
bool ctrlDown = source.GetKeyState(winrt::Windows::System::VirtualKey::Control) ==
818+
winrt::Windows::UI::Core::CoreVirtualKeyStates::Down;
819+
bool altDown = source.GetKeyState(winrt::Windows::System::VirtualKey::Control) ==
820+
winrt::Windows::UI::Core::CoreVirtualKeyStates::Down;
821+
bool metaDown = source.GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) ==
822+
winrt::Windows::UI::Core::CoreVirtualKeyStates::Down ||
823+
source.GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) ==
824+
winrt::Windows::UI::Core::CoreVirtualKeyStates::Down;
825+
return (submitKeyEvent.shiftKey && shiftDown) || (submitKeyEvent.ctrlKey && ctrlDown) ||
826+
(submitKeyEvent.altKey && altDown) || (submitKeyEvent.metaKey && metaDown) ||
827+
(!submitKeyEvent.shiftKey && !submitKeyEvent.altKey && !submitKeyEvent.metaKey && !submitKeyEvent.altKey &&
828+
!shiftDown && !ctrlDown && !altDown && !metaDown);
829+
} else {
830+
shouldSubmit = false;
831+
}
832+
} else {
833+
shouldSubmit = false;
834+
}
835+
}
836+
return shouldSubmit;
837+
}
838+
801839
void WindowsTextInputComponentView::OnCharacterReceived(
802840
const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source,
803841
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
@@ -809,6 +847,24 @@ void WindowsTextInputComponentView::OnCharacterReceived(
809847
return;
810848
}
811849

850+
// Logic for submit events
851+
if (ShouldSubmit(source, args)) {
852+
// call onSubmitEditing event
853+
if (m_eventEmitter && !m_comingFromJS) {
854+
auto emitter = std::static_pointer_cast<const facebook::react::WindowsTextInputEventEmitter>(m_eventEmitter);
855+
facebook::react::WindowsTextInputEventEmitter::OnSubmitEditing onSubmitEditingArgs;
856+
onSubmitEditingArgs.text = GetTextFromRichEdit();
857+
onSubmitEditingArgs.eventCount = ++m_nativeEventCount;
858+
emitter->onSubmitEditing(onSubmitEditingArgs);
859+
}
860+
861+
if (m_clearTextOnSubmit) {
862+
// clear text from RichEdit
863+
m_textServices->TxSetText(L"");
864+
}
865+
return;
866+
}
867+
812868
WPARAM wParam = static_cast<WPARAM>(args.KeyCode());
813869
LPARAM lParam = 0;
814870
lParam = args.KeyStatus().RepeatCount; // bits 0-15
@@ -926,6 +982,7 @@ void WindowsTextInputComponentView::updateProps(
926982
}
927983

928984
if (oldTextInputProps.multiline != newTextInputProps.multiline) {
985+
m_multiline = newTextInputProps.multiline;
929986
propBitsMask |= TXTBIT_MULTILINE | TXTBIT_WORDWRAP;
930987
if (newTextInputProps.multiline) {
931988
propBits |= TXTBIT_MULTILINE | TXTBIT_WORDWRAP;
@@ -952,6 +1009,16 @@ void WindowsTextInputComponentView::updateProps(
9521009
updateCursorColor(newTextInputProps.cursorColor, newTextInputProps.textAttributes.foregroundColor);
9531010
}
9541011

1012+
if (oldTextInputProps.clearTextOnSubmit != newTextInputProps.clearTextOnSubmit) {
1013+
m_clearTextOnSubmit = newTextInputProps.clearTextOnSubmit;
1014+
}
1015+
1016+
if ((!newTextInputProps.submitKeyEvents.empty())) {
1017+
m_submitKeyEvents = newTextInputProps.submitKeyEvents;
1018+
} else {
1019+
m_submitKeyEvents.clear();
1020+
}
1021+
9551022
/*
9561023
if (oldTextInputProps.textAttributes.foregroundColor != newTextInputProps.textAttributes.foregroundColor) {
9571024
if (newTextInputProps.textAttributes.foregroundColor)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ struct WindowsTextInputComponentView : WindowsTextInputComponentViewT<WindowsTex
109109
void updateCursorColor(
110110
const facebook::react::SharedColor &cursorColor,
111111
const facebook::react::SharedColor &foregroundColor) noexcept;
112+
bool ShouldSubmit(
113+
const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source,
114+
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept;
112115

113116
winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr};
114117
winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr};
@@ -132,6 +135,9 @@ struct WindowsTextInputComponentView : WindowsTextInputComponentViewT<WindowsTex
132135
int m_cDrawBlock{0};
133136
bool m_needsRedraw{false};
134137
bool m_drawing{false};
138+
bool m_clearTextOnSubmit{false};
139+
bool m_multiline{false};
140+
std::vector<facebook::react::CompWindowsTextInputSubmitKeyEventsStruct> m_submitKeyEvents;
135141
};
136142

137143
} // namespace winrt::Microsoft::ReactNative::Composition::implementation

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,14 @@ void WindowsTextInputEventEmitter::onSelectionChange(const OnSelectionChange &ev
2828
});
2929
}
3030

31+
void WindowsTextInputEventEmitter::onSubmitEditing(OnSubmitEditing event) const {
32+
dispatchEvent("textInputSubmitEditing", [event = std::move(event)](jsi::Runtime &runtime) {
33+
auto payload = jsi::Object(runtime);
34+
payload.setProperty(runtime, "eventCount", event.eventCount);
35+
payload.setProperty(runtime, "target", event.target);
36+
payload.setProperty(runtime, "text", event.text);
37+
return payload;
38+
});
39+
}
40+
3141
} // namespace facebook::react

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@ class WindowsTextInputEventEmitter : public ViewEventEmitter {
2626
Selection selection;
2727
};
2828

29+
struct OnSubmitEditing {
30+
int eventCount;
31+
int target;
32+
std::string text;
33+
};
34+
2935
void onChange(OnChange value) const;
3036
void onSelectionChange(const OnSelectionChange &value) const;
37+
void onSubmitEditing(OnSubmitEditing value) const;
3138
};
3239

3340
} // namespace facebook::react

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ static inline std::string toString(const CompWindowsTextInputSelectionStruct &va
3333
}
3434

3535
struct CompWindowsTextInputSubmitKeyEventsStruct {
36-
bool altKey;
37-
bool ctrlKey;
38-
bool metaKey;
39-
bool shiftKey;
36+
bool altKey{false};
37+
bool ctrlKey{false};
38+
bool metaKey{false};
39+
bool shiftKey{false};
4040
std::string code;
4141
};
4242

0 commit comments

Comments
 (0)