Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KeyboardToolbar unusable when used inside RN Modal #588

Open
aaamoshd opened this issue Sep 18, 2024 · 14 comments
Open

KeyboardToolbar unusable when used inside RN Modal #588

aaamoshd opened this issue Sep 18, 2024 · 14 comments
Assignees
Labels
🐛 bug Something isn't working KeyboardToolbar Anything related to KeyboardToolbar component 🌎 modal Anything that involves Modal usage

Comments

@aaamoshd
Copy link

Describe the bug
When KeyboardToolbar component is used inside a react-native modal the next, prev and done buttons become un-clickable and when clicked just closes the keyboard. No matter of z-index in the button props seem to be doing the trick.

Code snippet

<Modal
  presentationStyle="formSheet"
  onRequestClose={onClose}
  visible={open}
  transparent={false}
>
  <KeyboardAvoidingView
      behavior="padding"
      keyboardVerticalOffset={100}
      style={{ flex: 1, paddingHorizontal: 16 }}
  >
      <Input
          required
          onChangeText={(val) =>
              setValue({
                  ...value,
                  data: val,
              })
          }
          value={value.data}
      />
      <Button color="tertiary" onPress={handleSubmit}>
          <Typography
              color="primary"
              weight="600"
              align="center"
              style={{
                  textTransform: 'uppercase',
                  width: '100%',
              }}
          >
              {localize.translate('add')}
          </Typography>
      </Button>
   </KeyboardAvoidingView>
    <KeyboardToolbar
        button={(props) => (
            <Button {...props} style={{ zIndex: 1000 }} />
        )}
    />
</Modal>

Repo for reproducing
Sorry since this is an internal project, I am unable to provide you with the reproducing repo. However, if you install the package, wrap app with and then open a modal with the component from this library along with KeyboardToolbar the navigation buttons become unusable.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new RN project
  2. Install react-native-keyboard-controller
  3. Wrap your app with KeyboardProvider
  4. On one of the screen use Modal to show form fields and inject KeyboardToolbar
  5. Try clicking on the toolbar next, prev and done button.

Expected behavior
To be able to click on the toolbar to be able to navigate form fields.

Screenshots
MyMovie1-ezgif com-video-to-gif-converter

Smartphone (please complete the following information):

  • Desktop OS: [MacOS 14.4.1]
  • Device: [All Device]
  • OS: [ios ~17, android ~ 14]
  • RN version: [e.g. 0.74.0]
  • RN architecture: [fabric]
  • JS engine: [Hermes]
  • Library version: [1.13.4]

Additional context
The library is super awesome. It works on all the other screens apart from when being used inside a Modal.

@kirillzyusko
Copy link
Owner

@aaamoshd just for a context - it doesn't work on iOS only or both iOS and Android?

@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🌎 modal Anything that involves Modal usage KeyboardToolbar Anything related to KeyboardToolbar component labels Sep 18, 2024
@aaamoshd
Copy link
Author

@kirillzyusko for me it does not work on both iOS and Android. Sorry missed that in the report.

@kirillzyusko
Copy link
Owner

@aaamoshd I'm afraid I need a reproduction repo. I've added a Modal in my code and it works well on iOS:

Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-09-18.at.18.22.01.mp4

On Android it indeed doesn't work (focus can not be set 🤷‍♂️ )

Do you have the same problem?

@aaamoshd
Copy link
Author

aaamoshd commented Sep 18, 2024

wow that is one weird behavior.
So I do have SafeAreaProvider and SafeAreaView wrapping my KeyboardProvider. Is this the result of not using KeyboardProvider as the root wrapper?
You are able to easily navigate however whenever I try navigating, all it does is close my keyboard. Also I wanted to know if this example is based on react-navigation and setting screen's presentation as modal.

@aaamoshd
Copy link
Author

hi @kirillzyusko,

I got the library working on iOS by adding the multiline property to the inputs and making the prop dependent on a state variable within the RN Modal. However, I'm still running into issues with Android and haven’t had any luck so far. I was hoping you could help me understand why adding this change made things work on iOS but not Android.

Additionally, if we create a screen and set its presentation to 'modal' (instead of using RN Modal), the toolbar works on both iOS and Android. My assumption is that since it's just a screen and not a true Modal, this is why it works. Could the library potentially need something similar to how react-native-gesture-handler wraps components in a HOC for Android Modals?

One more thing I noticed on iOS is that the toolbar doesn't stick perfectly to the top of the keyboard—it sits slightly higher. I suspect it might be a configuration issue on my end, but I'd really appreciate it if you could point me in the right direction.

Below is the behavior of the toolbar comparing Android and iOS.

Android Toolbar (Works Fine)
Screenshot_1726767077

iOS Toolbar is slightly higher and is not sticky to the view.
Simulator Screenshot - iPhone 15 Pro Max - 2024-09-19 at 10 31 24

Thanks again for your time and for the great work!

@kirillzyusko
Copy link
Owner

kirillzyusko commented Sep 19, 2024

However, I'm still running into issues with Android and haven’t had any luck so far. I was hoping you could help me understand why adding this change made things work on iOS but not Android.

Well, if you can provide a minimal reproduction example - then I'll prioritise this issue over others and will try to fix it ASAP. Without a proper reproduction repository/code I feel like I may do something wrong and just waste time and not reproduce it at all 😔

Additionally, if we create a screen and set its presentation to 'modal' (instead of using RN Modal), the toolbar works on both iOS and Android. My assumption is that since it's just a screen and not a true Modal, this is why it works.

Yeah, you are right. react-navigation most likely just uses internal replica and doesn't use Modal component.

Could the library potentially need something similar to how react-native-gesture-handler wraps components in a HOC for Android Modals?

Well, if you can discover a focused input (and on Android you can easily do that by setting up a global listener), then you should be able to find sibling elements as well, so I believe there is no need to wrap a Modal. Moreover this library tracks all events when Modal becomes shown, so I may inject something without additional user actions 🤫

One more thing I noticed on iOS is that the toolbar doesn't stick perfectly to the top of the keyboard—it sits slightly higher. I suspect it might be a configuration issue on my end, but I'd really appreciate it if you could point me in the right direction.

This component should be placed straight behind the bottom corner (where keyboard starts its movement) of the screen, i. e. ignoring safe area insets. On you screen I believe it's placed behind bottom safe area inset. So basically you have two options:

  • try to place it ignoring safe area insets
  • underhood KeyboardToolbar relies on KeyboardStickyView. And KeyboardStickyView has offset property. KeyboardToolbar internally uses this property, but doesn't expose that property to public (a good question "why?" - potentially props can be merged together, and I think I had this task somewhere in my ToDo list 😅)

If you can prepare a simple reproduction example and show a case when both iOS and Android doesn't work properly, then I promise I'll try to fix it in nearest time 🙏

I understand that the scenario for reproduction seems to be a very simple, but again, in my case only Android/fabric is reproducible. And I have a gut feeling that even if I may fix a problem on my end it doesn't mean that it'll be fixed for your use case. So the reproduction example is highly appreciated and can significantly reduce a time for fixing this problem! ❤️

P. S.

Also I wanted to know if this example is based on react-navigation and setting screen's presentation as modal.

No, I used Modal component from react-native. Do you use the same component when it breaks in your case?

@kirillzyusko
Copy link
Owner

@aaamoshd can you please check if #590 fixes the problem on Android?

@aaamoshd
Copy link
Author

Hi @kirillzyusko I just tried using #590 on my project but still facing the same issue on android where <KeyboardToolbar /> can not be used when opened in a Modal component. I was wondering as to how I could provide you with my problem repository but since it has a lots of custom module, custom components it might be difficult to create a reproduction. Any insight on what particular setting or configuration would be most useful and maybe we can plan accordingly.

Also interestingly enough, ran into similar bug as of #387 in android, where once I open a keyboard on a screen and then try to open a Modal it was crashing the app or the modal would make the app unusable. The Modal would be behind the current screen but then the entire app would just freeze. But once the utility function was added to wait for the keyboard close before opening a modal and then wrapped the modal within View fixed the issue for me.

@kirillzyusko
Copy link
Owner

I just tried using #590 on my project but still facing the same issue on android where can not be used when opened in a Modal component.

Well, yeah, that's hat I wrote 😔 :

And I have a gut feeling that even if I may fix a problem on my end it doesn't mean that it'll be fixed for your use case.

I was wondering as to how I could provide you with my problem repository but since it has a lots of custom module, custom components it might be difficult to create a reproduction. Any insight on what particular setting or configuration would be most useful and maybe we can plan accordingly.

Well, if I were you I would try to remove components one by one, and replace all wrappers with plain RN components (i. e. Typography with Text etc.). In the end you will get a code sample, that will not rely on navigation, etc. and this code snippet can be simply pasted in App.tsx and it should be reproducible in any empty project. If you can give me such code sample then I can try to put it in my example project and see how I can reproduce the problem.

Also interestingly enough, ran into similar bug as of #387 in android, where once I open a keyboard on a screen and then try to open a Modal it was crashing the app or the modal would make the app unusable. The Modal would be behind the current screen but then the entire app would just freeze. But once the utility function was added to wait for the keyboard close before opening a modal and then wrapped the modal within View fixed the issue for me.

Well, this one is also interesting. I fixed an integration with Modal and added a test screen in example app and covered it by e2e tests. Basically the Modal should work 🤷‍♂️ It's very hard to say what exactly goes in a wrong direction just because Modal involves too many things in one component, so reproduction example can shed some light on the problem 👀 But if you decide to post the repro for Modal problem, then please - open a new issue. This problem sounds slightly different to a current one, and I'd like to keep separated problems separately!

@aaamoshd
Copy link
Author

aaamoshd commented Sep 21, 2024

hi @kirillzyusko I think you may have fixed the issue regarding toolbar and android when used inside the modal.
I was trying to create a minimal reproduction repository and saw that the package worked for both android and iOS. So got interested into seeing why there was an issue on my project. I cleared all the cache and performed a clean install of the fix #590. Found out that the issue was with the screen where the modal was being injected. Turns out very first <ScrollView> needs to have keyboardShouldPersistTaps={'always'} and keyboardShouldPersistTaps={'handled'} on the <KeyboardAwareScrollView> inside the <Modal> component.

Essentially, Screen index:

<ScrollView keyboardShouldPersistTaps={'always'}>
{... other UI components}
<Modal isVisible={open} {...}>
<SafeAreaView>
    <KeyboardAwareScrollView keyboardShouldPersistTaps={'handled'}>
    </KeyboardAwareScrollView>
</SafeAreaView>
<KeyboardToolbar /> //this needs to be here injected to the modal directly
</Modal>
</ScrollView>

However, I do not have issues with forms that are directly on the screens and they work without having to use the prop keyboardShouldPersistTaps.

As far for the other issue with opening modal while keyboard is open, I wrote a wrapper component for my modals and set a watcher on the isVisible property. Then on android, I call the utility function mentioned in #387 to close the keyboard before opening the modal. Everything seems to be working fine.

@kirillzyusko
Copy link
Owner

I see @aaamoshd

So to sum it up:

  • if you show a Modal and this Modal is inside ScrollView, then touches will not be captured by KeyboardToolbar. Instead touches will be intercepted by outer ScrollView and will simply dismiss the keyboard - I believe I can not fix this problem in package, because it's a clear conflict of ScrollView and Modal responders 🤷‍♂️ In my understanding your code should work without modifying keyboardShouldPersistTaps properties, because Modals are shown in separate hierarchy and outer ScrollView shouldn't intercept touches if Modal is shown. A potential workaround would be to modify keyboardShouldPersistTaps of outer ScrollView only if Modal gets shown 🤔
    Or if possible try to show Modal not inside the ScrollView 🙃
  • if we remove ScrollView, then arrow press doesn't do anything on Android - I believe it's fixed by fix: KeyboardToolbar in Modal on Android #590 right?

As far for the other issue with opening modal while keyboard is open, I wrote a wrapper component for my modals and set a watcher on the isVisible property.

What is the problem? That Modal is not stretched to full window screen? I. e. you see something like:

image

@aaamoshd
Copy link
Author

aaamoshd commented Sep 24, 2024

Hi @kirillzyusko:
Yup, at least thats what my analysis resulted in terms of the Keyboard Toolbar behavior inside a modal.

Or if possible try to show Modal not inside the ScrollView

Most of my screens follow a certain layout and one of the component defined in the layout is a <ScrollView>. It is essential for the app to have that layout which means the logic to display a modal is defined inside the children passed in the layout.

Also, looks like for the android even with the #590 fix, we need to dirty the form once (either type in the input or click on a different input once the keyboard opens) for it to allow navigation otherwise it does not focus properly. Once I dirty an input that opened the keyboard I can navigate using the toolbar. any idea as to why its happening? It only happens when using a release build on a physical device, on a simulator it works just fine. Also, looks like it only happens first time you open the app and try using the keyboard.

What is the problem? That Modal is not stretched to full window screen? I. e. you see something like:

Yeah, I think thats what's happening, Keyboard would close and I wouldn't even see the modal but I know the component was made visible just the whole screen would freeze and I would have to close out the app.

@aaamoshd
Copy link
Author

As a workaround I tried passing a custom button props as

<KeyboardToolbar
  button={(props) => (
      <TouchableOpacity
          {...props}
          onPress={undefined}
          onPressIn={props.onPress}
      />
  )}
/>

and it worked out. So, thinking this has to do again with ScrollView and it managing taps.

@kirillzyusko
Copy link
Owner

Once I dirty an input that opened the keyboard I can navigate using the toolbar. any idea as to why its happening? It only happens when using a release build on a physical device, on a simulator it works just fine. Also, looks like it only happens first time you open the app and try using the keyboard.

Hm, this sounds very interesting! 🤔

May I close this problem (since we figured out, that custom button + #590 fixes the problem) and ask you to open separate issues for other problems (i. e. dirtying fields, incorrect modal layout, and any other but keep all of them separate)?

I would be glad to fix them, but don't really want to mix different problems in a single issue, because they become very big and for people who searches for similar issues it'll be hard to read all that information. What do you think?

Sorry for a long response - I was sick, so couldn't respond quickly to any issues 😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working KeyboardToolbar Anything related to KeyboardToolbar component 🌎 modal Anything that involves Modal usage
Projects
None yet
Development

No branches or pull requests

2 participants