Add shouldDismissKeyboardOnTap property to ScrollView - #57875
Conversation
|
Hi @m-bert! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Warning JavaScript API change detected This PR commits an update to
This change was flagged as: |
There was a problem hiding this comment.
Pull request overview
This PR introduces an opt-in shouldDismissKeyboardOnTap callback on ScrollView to refine keyboardShouldPersistTaps="handled" behavior, allowing non-responder-based touch handlers (e.g. native recognizers / gesture-handler buttons) to prevent keyboard dismissal for specific taps without reimplementing ScrollView internals.
Changes:
- Add
shouldDismissKeyboardOnTapprop and consult it as an additional condition for the"handled"responder-claim path. - Update TypeScript/public API typings to include the new prop.
- Add Jest coverage validating default behavior, veto behavior, and non-consulted cases.
Reviewed changes
Copilot reviewed 2 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/react-native/Libraries/Components/ScrollView/ScrollView.js | Adds the shouldDismissKeyboardOnTap prop and integrates it into "handled" responder-claim logic. |
| packages/react-native/Libraries/Components/ScrollView/ScrollView.d.ts | Exposes the new prop in the public TypeScript ScrollViewProps definition. |
| packages/react-native/ReactNativeApi.d.ts | Updates the generated API snapshot to reflect the new ScrollView prop. |
| packages/react-native/Libraries/Components/ScrollView/tests/ScrollView-shouldDismissKeyboardOnTap-test.js | Adds unit tests for default claim, veto behavior, and non-consulted scenarios. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| async function renderScrollView(props: ScrollViewProps) { | ||
| let testRenderer; | ||
| await ReactTestRenderer.act(() => { | ||
| testRenderer = ReactTestRenderer.create(<ScrollView {...props} />); | ||
| }); | ||
|
|
||
| const instance = (testRenderer as $FlowFixMe).root.find( | ||
| node => node.instance?._handleStartShouldSetResponder != null, | ||
| ).instance as $FlowFixMe; | ||
|
|
||
| return instance; | ||
| } | ||
|
|
||
| describe('shouldDismissKeyboardOnTap', () => { | ||
| beforeEach(() => { | ||
| // Simulate a focused text input with an open soft keyboard. | ||
| TextInputState.registerInput(fakeTextInput); | ||
| TextInputState.focusInput(fakeTextInput); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| TextInputState.blurInput(fakeTextInput); | ||
| TextInputState.unregisterInput(fakeTextInput); | ||
| }); |
Summary:
While fixing software-mansion/react-native-gesture-handler#4328 we found that ScrollView with
keyboardShouldPersistTaps="handled"decides whether a tap was "handled" purely through the responder system: if no child claims the start-should-set negotiation,ScrollViewclaims the responder and dismisses the keyboard on release. Components that handle touches outside of the responder system (native gesture recognizers, Gesture Handler buttons, etc.) never claim the responder, so taps on them always dismiss the keyboard. We got out of it withdisableScrollViewPanResponder+ reimplementing the 'handled' dismissal ourselves, but that means copyingScrollViewinternals.This PR adds an opt-in
shouldDismissKeyboardOnTapprop, consulted as one extra condition in the existing"handled"claim. Returningfalsemarks the tap as already handled and keeps the keyboard up. When the prop is not set, behavior is unchanged. The prop has no effect in"never"and"always"modes.Changelog:
[GENERAL] [ADDED] - Add
shouldDismissKeyboardOnTapprop toScrollViewTest Plan:
ScrollView-shouldDismissKeyboardOnTap-test.jscovering the default claim, the veto, and the cases where the callback must not be consulted (keyboard down, tap on the focused input,"never"/"always"modes). Ran withyarn jest packages/react-native/Libraries/Components/ScrollView.yarn flow check,yarn lint,yarn build-types(ReactNativeApi.d.tssnapshot updated; addition only, no changes to existing API shapes).react-nativeand rewiredreact-native-gesture-handlerto passshouldDismissKeyboardOnTap={() => !handledByGesture}. Taps on Gesture Handler buttons and gesture detectors keep the keyboard up, taps on empty space dismiss it, andstickyHeaderIndiceskeeps working.