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

Fix the keyboard overlapping buttons on the sign in form #12848

Merged
merged 17 commits into from
Dec 5, 2022

Conversation

tgolen
Copy link
Contributor

@tgolen tgolen commented Nov 17, 2022

Details

The page was originally built with using a top margin set to a percentage of the page height. Because of that, when the keyboard opens, the page wasn't properly collapsing the top margin in order to reveal the entire form. I restructured the entire layout to properly use flex techniques to allow the section to collapse as intended.

Fixed Issues

$ #11701

Tests

  1. Be signed out
  2. Have your email address entered so that you see the password form
  3. For native platforms, verify the password form is not covered by the software keyboards
  4. For all other platforms, verify that the form and page layout looks correct (best to compare it to production).

For web specifically:

  1. Test the page at widths less than 800 (form should always be centered and have a max width of 400px. The form should NOT span the entire width of the page for widths between 400 and 800).
  2. Test the page at widths between 801 and 1024 (the form should no longer take up the top half of the page and it should look just like any width greater than 1024)
  3. Test the page at widths greater than 1024 (form should be a fixed width of ~400px on the left side of the page)

Test keyboard dismissing on native (iOS and Android):

  1. On both the enter email and enter password forms, tap into the text input
  2. Tap outside of the input and verify the keyboard is dismissed and verify the input does not have focus (it does not have a blue border)
  3. On the enter email form, enter the email of an existing account and either tap continue or press enter
  4. Verify the keyboard remains shown on the password form and the password text input has focus
  5. On the email form, enter an email address that doesn't have an account yet and press enter (or click continue)
  6. Verify that you are shown the message that a link has been sent to you and verify the keyboard has been dismissed
  • Verify that no errors appear in the JS console

Offline tests

  1. Go offline
  2. Verify there is an offline indicator under the sign in button
  3. Go online
  4. Verify the offline indicator goes away

QA Steps

Same as the tests above

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

PR Reviewer Checklist

The reviewer will copy/paste it into a new comment and complete it after the author checklist is completed

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for expected offline behavior are in the Offline steps section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Web

2022-11-28_15-24-44
image

Mobile Web - Chrome

2022-11-28_15-25-57
image

Mobile Web - Safari

2022-11-28_15-27-06

image

Desktop

2022-11-28_15-27-50

iOS

2022-11-28_15-26-53
image

Android

2022-11-28_15-25-03
image

@tgolen tgolen self-assigned this Nov 17, 2022
@tgolen tgolen changed the title Tgolen fix signin keyboard avoiding Fix the keyboard overlapping buttons on the sign in form Nov 18, 2022
@tgolen tgolen marked this pull request as ready for review November 18, 2022 16:49
@tgolen tgolen requested a review from a team as a code owner November 18, 2022 16:49
@melvin-bot melvin-bot bot requested review from aimane-chnaif and cristipaval and removed request for a team November 18, 2022 16:50
@melvin-bot
Copy link

melvin-bot bot commented Nov 18, 2022

@aimane-chnaif @cristipaval One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@rushatgabhane
Copy link
Member

@tgolen could i please review this PR because i was assigned to the issue for this PR

@rushatgabhane
Copy link
Member

rushatgabhane commented Nov 18, 2022

@tgolen this might be a dumb question - how to dismiss keyboard on iOS? (i tried drag outside text input)

usecase: See the footer which has ToS and language selector

a.mov

@tgolen tgolen requested a review from rushatgabhane November 18, 2022 19:02
@tgolen
Copy link
Contributor Author

tgolen commented Nov 18, 2022

@rushatgabhane Yeah, feel free to review! I've gone and added you.

I have fixed the keyboard dismissing issue (had to add back in the scrollview).

@melvin-bot
Copy link

melvin-bot bot commented Nov 18, 2022

Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers!

@aimane-chnaif
Copy link
Contributor

aimane-chnaif commented Nov 18, 2022

Shall I review this PR as I am assigned?

@tgolen
Copy link
Contributor Author

tgolen commented Nov 18, 2022

Anyone can review it :D The more the merrier!

Copy link
Contributor

@cristipaval cristipaval left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tgolen ! I tested your branch and looks like the submit button is still partially covered by the keyboard.

Screen.Recording.2022-11-21.at.17.19.51.mov

This is NexusOne with Android 10

@rushatgabhane
Copy link
Member

@cristipaval could you please re-upload the recording, thanks!

@cristipaval
Copy link
Contributor

@cristipaval could you please re-upload the recording, thanks!

should work now

@tgolen
Copy link
Contributor Author

tgolen commented Nov 21, 2022

Thanks @cristipaval! I think that will be OK. I don't think that's a supported device or android version? Is that what you normally test with?

@cristipaval
Copy link
Contributor

cristipaval commented Nov 21, 2022

This is an emulator on which I usually test to see how the App behaves on smaller screens. The Android 10 it's not old I'd say, so we should support it.

I do agree that the behaviour is still fine, even if the button is completely covered by the keyboard, as long as we have the submit action on the keyboard working as a physical tap on the submit button.

@tgolen
Copy link
Contributor Author

tgolen commented Nov 21, 2022

I checked with QA and they are only required to test on Android 11 and above, so I think what you're seeing there is OK.

@cristipaval
Copy link
Contributor

Yes it works better on newer Android versions.

Looks like howsVerticalScrollIndicator={false} does not work on my Pixel 5 emulator with Android 12.

Screen.Recording.2022-11-21.at.19.35.57.mov

@aimane-chnaif
Copy link
Contributor

signin.mp4

In video, 00:00-00:06 is before fix, 00:07-00:13 is after fix
@tgolen 3 issues found here (tested on Samsung Galaxy S10, Android 12 - popular device)

  • scroll indicator position - this can be moved to edge
  • screen is a bit pushed to top compared to old version
  • design is different from old one, i.e. horizontal paddings
    Totally, I found no flaws with old native android app but while you're fixing it to be responsive to mChrome, I feel screen looks worse than before.

@tgolen
Copy link
Contributor Author

tgolen commented Nov 21, 2022

OK, thanks for the testing! I'll work on those today and tomorrow and see what I come up with.

@tgolen
Copy link
Contributor Author

tgolen commented Nov 21, 2022

Oh, ha... typo in the prop for howsVerticalScrollIndicator 🤦

@tgolen
Copy link
Contributor Author

tgolen commented Nov 22, 2022

Branch updated! I believe I got everything addressed:

  • Keyboard dismisses when tapping outside of inputs (and stays open when transitioning between the email and password form)
  • Fixed the visible scrollbar (by removing the scroll view entirely)
  • Fixed the padding to match production
  • Updated tests to try and cover all the cases that need to be accounted for

jasperhuangg
jasperhuangg previously approved these changes Nov 22, 2022
Copy link
Contributor

@jasperhuangg jasperhuangg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just noticed some typos, overall looks good

<SignInPageForm style={[
const SignInPageContent = (props) => {
const dismissKeyboardWhenTappedOutsideOfInput = () => {
// This prop comes from with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This prop comes from with
// This prop comes from withKeyboardState

props.isSmallScreenWidth ? styles.ph5 : styles.ph4,
styles.signInPageLeftContainer,

// Restrict the width if the left container only for large screens. For smaller screens, the width needs to be fluid to span the entire width of the page.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Restrict the width if the left container only for large screens. For smaller screens, the width needs to be fluid to span the entire width of the page.
// Restrict the width of the left container only for large screens. For smaller screens, the width needs to be fluid to span the entire width of the page.

styles.signInPageLeftContainer,

// Restrict the width if the left container only for large screens. For smaller screens, the width needs to be fluid to span the entire width of the page.
!props.isMediumScreenWidth && !props.isSmallScreenWidth && styles.signInPageWideLeftContainer,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: Wondering why we don't have a isLargeScreenWidth prop for times like these

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, that's a great point. I want to refactor it a little to have this.

@tgolen
Copy link
Contributor Author

tgolen commented Dec 1, 2022

Ack, I just realized I didn't push up the code for that last change. Anyway, the code is there now 😅

signInPageLeftContainerMediumScreen: {
maxWidth: 400,
marginLeft: 'auto',
marginRight: 'auto',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+        marginLeft: 'auto',
+        marginRight: 'auto',

What is this change for?
Because of this, horizontal padding is not consistent between email page and password page
Simulator Screen Shot - iPhone SE (3rd generation) - 2022-12-01 at 12 08 54
Simulator Screen Shot - iPhone SE (3rd generation) - 2022-12-01 at 12 09 03
tested on iPhone SE
you can see button width are different

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, this stuff is so finicky.

The reason I added the auto-margins is:

  1. Shawn requested to have the form use a max-width
  2. Setting a maxWidth works for the form, but for medium-sized screens, the form is aligned on the left side of the container and looks bad. The form needs to be horizontally centered in the container.
  3. My first thought was to use alignSelfCenter, and that works. BUT at small screen widths, that style also centers the content vertically 😡 and adds a lot of unintended margin to the top and bottom
  4. So I went with the auto margin route, which makes the form horizontally centered AND doesn't mess up any vertical margins

I have fiddled with it again today to remove the margin auto and get the alignSelfCenter working.

I narrowed the problem you saw with iPhone SE down to the props.welcomeText. It seems the difference in text was causing the width of the container to change size... so I have restricted that text to 300px wide always.

Updated!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you (or a reviewer) share a quick video of the sign in page going from small to wide? Just want to check and see how the responsive styles are working, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you go:

2022-12-01_12-50-20.mp4

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, looks good to me, thanks for making the video!

@aimane-chnaif
Copy link
Contributor

@tgolen please pull from latest main and fix conflict once more

@tgolen
Copy link
Contributor Author

tgolen commented Dec 2, 2022

Updated to resolve conflicts.

@aimane-chnaif
Copy link
Contributor

doing final test now

@aimane-chnaif
Copy link
Contributor

The keyboard "glitch" where the keyboard hides and then reshows quickly when going from the username to password form
IGNORED I was not able to solve this. I went so far as to remove every usage of Keyboard.dismiss() in our app, and I couldn't find anything that was causing the keyboard to dismiss. It's possible it's happening as part of our navigation library. It is also happening in main so it's not a regression in this PR and I think it's OK to merge.

NOTE: This is already logged here and will be fixed in this PR

@aimane-chnaif
Copy link
Contributor

Looks good to me and tests well on all platforms with various screen sizes (except android chrome on very small device commented here)

Copy link
Contributor

@jasperhuangg jasperhuangg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks great and tests well on all platforms! Unable to replicate the broken small-screen android case, but I imagine that's an edge case so I'm approving. Let's create a follow-up issue for that?

@JmillsExpensify
Copy link

Quick follow-up up coming from the main issue. This PR looks ready to merge aside from @jasperhuangg's point above. Let's get this one merged today!

@tgolen
Copy link
Contributor Author

tgolen commented Dec 5, 2022

Yep, since it's been twice approved, I'll go ahead and self-merge this so we can finally get it done.

@tgolen tgolen merged commit 5530ea2 into main Dec 5, 2022
@tgolen tgolen deleted the tgolen-fix-signin-keyboard-avoiding branch December 5, 2022 14:49
@OSBotify
Copy link
Contributor

OSBotify commented Dec 5, 2022

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify
Copy link
Contributor

🚀 Deployed to production by @chiragsalian in version: 1.2.38-6 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@chiragsalian
Copy link
Contributor

Just FYI that this PR caused #13488.

@eVoloshchak
Copy link
Contributor

eVoloshchak commented Feb 6, 2023

Leaving a note that this PR caused #13920

This is some weird quirk with RNPickerSelect where when you enclose them with Touchables it makes them untouchable.

@rushatgabhane
Copy link
Member

@eVoloshchak could you please explain why it caused it?

@eVoloshchak
Copy link
Contributor

@rushatgabhane, sure!
On this line the sign-in page is wrapped with TouchableWithoutFeedback. It seems like there is an issue with RNPickerSelect when you enclose in with touchable on Android. It seems like it was enclosed with touchable to fix the issue specific to iOS

 * On iOS sometimes the keyboard doesn't dismiss itself
 * when you tap outside of the input that pulled it up.
 * Therefore this component is necessary to ensure users
 * can still dismiss the keyboard on iOS.

So it was resolved by using platform-specific files and using touchable only on iOS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants