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

Make login form accessible to Password Managers #5275

Merged
merged 30 commits into from
Dec 6, 2021

Conversation

parasharrajat
Copy link
Member

@parasharrajat parasharrajat commented Sep 15, 2021

Details

Fixed Issues

$ #3855
$ #5956

Tests | QA Steps

  1. Navigate to NewDot on the web.
  2. Right-click on the email field.
  3. Click on the 1Password option to show the autofill.
  4. 1Password Autofill option should be shown on the input.

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

Updating Shortly...

Mobile Web

Desktop

iOS

Android

@parasharrajat parasharrajat requested a review from a team as a code owner September 15, 2021 19:55
@MelvinBot MelvinBot requested review from marcaaron and removed request for a team September 15, 2021 19:55
@parasharrajat parasharrajat changed the title Make login for accessible to Password Managers Make login form accessible to Password Managers Sep 15, 2021
@AndrewGable
Copy link
Contributor

Conflicts!

src/libs/TextInputUtils/index.js Outdated Show resolved Hide resolved
src/libs/TextInputUtils/index.js Outdated Show resolved Hide resolved
src/libs/TextInputUtils/index.js Outdated Show resolved Hide resolved
src/libs/TextInputUtils/index.native.js Outdated Show resolved Hide resolved
src/libs/TextInputUtils/index.native.js Outdated Show resolved Hide resolved
src/libs/TextInputUtils/index.native.js Outdated Show resolved Hide resolved
textContentType="password"
nativeID={getPasswordAutocompleteType()}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious but what do we need to set the nativeID for ?

Copy link
Member Author

Choose a reason for hiding this comment

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

For Attaching the label with input. This could be optional. There are a lot of uncertainties in the case of password Managers so I used everything that we generally do for login forms. Another reason was to just point out these things in the review and decide if they are necessary.


componentDidMount() {
// These native props are needed for Password Managers like LastPass
if (this.form) {
Copy link
Contributor

Choose a reason for hiding this comment

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

early return please

// These native props are needed for Password Managers like LastPass
if (this.form) {
setNativePropsWeb(this.form, 'method', 'post');
setNativePropsWeb(this.form, 'action', '/');
Copy link
Contributor

Choose a reason for hiding this comment

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

We should encapsulate this logic in a new kind of view then pass these as props. The component can then handle setting these native props with this componentDidMount() logic instead of having to repeat it everywhere we want to do this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also please add some comments about why method and action are required in this case

@@ -44,6 +45,9 @@ class BaseExpensiTextInput extends Component {
if (this.props.autoFocus && this.input) {
this.input.focus();
}
if (this.input && this.props.name) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If there is no input we should return early instead of checking on like 48 and 45

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, absolutely.

@parasharrajat
Copy link
Member Author

Thanks for the review Marc. I will resolve them as soon as possible.

@marcaaron
Copy link
Contributor

What's the status on this one @parasharrajat ?

@parasharrajat
Copy link
Member Author

Not yet updated. I will ping you when done.

@parasharrajat
Copy link
Member Author

@rushatgabhane Could you please review this and tell me if it works for you?

@rushatgabhane
Copy link
Member

Ideally, once 1P is used for entering the email address, the Password on the following page will be autofilled as well.

I tested this branch with Bitwarden as my password manager.
The password field in next page isn't autofilled.

Screencast.from.07-11-21.11-47-33.AM.+03.mp4

@rushatgabhane
Copy link
Member

For Android, I'm getting this error on launch.

Warning: Failed prop type: ExpensiTextInputLabel: prop type `for` is invalid; it must be a function, usually from the `prop-types` package, but received `string`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.
    in ExpensiTextInputLabel (at BaseExpensiTextInput.js:189)
    in RCTView (at View.js:34)
    in View (at BaseExpensiTextInput.js:177)
    in TouchableWithoutFeedback (at BaseExpensiTextInput.js:176)
    in RCTView (at View.js:34)
    in View (at BaseExpensiTextInput.js:170)
    in RCTView (at View.js:34)
    in View (at BaseExpensiTextInput.js:169)
    in BaseExpensiTextInput (at ExpensiTextInput/index.android.js:7)

@parasharrajat
Copy link
Member Author

@rushatgabhane Thanks for testing it. I have pushed new changes, please check again. Let's see I tested with lastpass.

@parasharrajat parasharrajat changed the title Make login form accessible to Password Managers [WIP]Make login form accessible to Password Managers Nov 7, 2021
@rushatgabhane
Copy link
Member

@parasharrajat Works perfectly! 💯

@parasharrajat
Copy link
Member Author

Thanks, @rushatgabhane. It would have not been possible without your help.

If you are curious to know, what was the issue?

  1. Password Managers do not track dom changes unless page changes. So when we were switching between the login field component to the password field component, the selection of login you made in the login page from the password manager is also lost during this transition.

To fix this, we have to keep all the form fields, login, and password in the dom, keeping the relation between them.

Also, as we use the multistep form, I have to visually hide the other dom nodes based on the current step to keep the UI intact.

@rushatgabhane
Copy link
Member

Thanks for the clarity. It makes sense now :)

@parasharrajat
Copy link
Member Author

I will soon finalize the PR. All changes are done. Cleanup is left.

@parasharrajat
Copy link
Member Author

@marcaaron Ready for review. Major refactor... 😄

isVisible: false,
};

const CollapsibleView = props => (
Copy link
Contributor

Choose a reason for hiding this comment

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

ToggleVisibilityView maybe? Collapsible makes me think of things like this -> https://github.com/Eliav2/react-native-collapsible-view

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, Thanks for the better name

import textInputWithNamepropTypes from './textInputWithNamepropTypes';

/**
* On web we need to set the native attribure name for accessiblity.
Copy link
Contributor

Choose a reason for hiding this comment

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

attribute

</CollapsibleView>
<CollapsibleView isVisible={showPasswordForm}>
{isVisible => <PasswordForm isVisible={isVisible} />}
</CollapsibleView>
Copy link
Contributor

Choose a reason for hiding this comment

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

After seeing this I think this utility would be a bit cleaner as an HOC. Passing the prop only to immediately consume it looks kind of funny.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm. Let me do it that way.

Copy link
Member Author

@parasharrajat parasharrajat Dec 2, 2021

Choose a reason for hiding this comment

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

Actually, prop is used to manage side effects based on visibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could maybe pass the prop through to the wrapped component?

src/pages/signin/PasswordForm.js Show resolved Hide resolved
src/pages/signin/LoginForm.js Show resolved Hide resolved
@parasharrajat
Copy link
Member Author

Ready.

@parasharrajat
Copy link
Member Author

Ready for review.

Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

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

Looks good - just have a few more suggestions.

src/components/withToggleVisibilityView.js Outdated Show resolved Hide resolved
src/pages/signin/LoginForm.js Outdated Show resolved Hide resolved
src/pages/signin/LoginForm.js Outdated Show resolved Hide resolved
* Clear Login from the state
*/
clearLogin() {
this.setState({login: ''}, this.input.clear);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why call clear on this? Is ExpensiTextInput not a controlled component?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah. It's not. I just saw that we don't pass the value directly. ExpensiTextInput is using value={this.value}. Actually, we never faced a situation to make it controlled.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh hmm weird I don't get it. We are passing a value - is it used or not ? 🤔

It seems like a bad habit to call this.input.clear every time we update a value passed to ExpensifyTextInput to be an empty string. Why do we need to do this?

Copy link
Member Author

@parasharrajat parasharrajat Dec 4, 2021

Choose a reason for hiding this comment

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

I wasn't using it before but after recent changes, ExpensifyTextInput value is not clearing on updating the state variable login. But as soon as you blur it, values clear. So I think that using this.value instead of the state variable is causing this issue on ExpensifyTextInput.
As far as I know, it's not affecting other parts of the app so I don't think that refactor is necessary. If we face this issue again then I would suggest making the ExpensifyTextInput controlled.

src/pages/signin/PasswordForm.js Outdated Show resolved Hide resolved
src/pages/signin/PasswordForm.js Show resolved Hide resolved
src/pages/signin/PasswordForm.js Outdated Show resolved Hide resolved
src/pages/signin/SignInPage.js Outdated Show resolved Hide resolved
@parasharrajat
Copy link
Member Author

Changes Done.

marcaaron
marcaaron previously approved these changes Dec 4, 2021
Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

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

LGTM, I'm not too sure about that this.input.clear usage - feels like something we should not have to worry about.

@parasharrajat
Copy link
Member Author

parasharrajat commented Dec 6, 2021

@Jag96 This is ready for review and merge.

Copy link
Contributor

@Jag96 Jag96 left a comment

Choose a reason for hiding this comment

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

Looks good and tests well, looks like just one copy-paste mistake

@@ -36,6 +36,7 @@ class ExpensiTextInputLabel extends PureComponent {
}
}

ExpensiTextInputLabel.propTypes = propTypes;
ExpensiTextInputLabel.propTypes = expensiTextInputLabelPropTypes.propTypes;
ExpensiTextInputLabel.propTypes = expensiTextInputLabelPropTypes.defaultProps;
Copy link
Contributor

Choose a reason for hiding this comment

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

ExpensiTextInputLabel.defaultProps =

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, Nice catch.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@Jag96 Jag96 merged commit 89d9dc1 into Expensify:main Dec 6, 2021
@OSBotify
Copy link
Contributor

OSBotify commented Dec 6, 2021

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

@parasharrajat
Copy link
Member Author

Unfortunately, found an issue with this PR.

The keyboard does not close when you switch between the login and password pages on Android. As a side-effect, View is not correctly placed above the keyboard when the page changes. It happens due to keyboardShouldPersistTaps="handled" prop. Previously unmounting the input was used to close the Keyboard.

I guess, we will have to manually blur the input when isvisible is false in LoginForm and PasswordForm. @Jag96 . I will spin up the PR tomorrow.

@OSBotify
Copy link
Contributor

OSBotify commented Dec 7, 2021

🚀 Deployed to staging by @Jag96 in version: 1.1.17-8 🚀

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

@OSBotify
Copy link
Contributor

OSBotify commented Dec 8, 2021

🚀 Deployed to production by @roryabraham in version: 1.1.18-3 🚀

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

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.

6 participants