-
Notifications
You must be signed in to change notification settings - Fork 472
Client side validation of Signup form #1251
Conversation
//popover css used in signup page for client side validation | ||
|
||
#signup_page .popover{ | ||
background-color: rgba(0,0,0,0.6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use 2 spaces and spaces after the commas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like this rgba(0, 0, 0, 0.6);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will use the 2 spaces for Indent level :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Also, move this to a separated file. It could be a registration.scss
in the pages
folder since you are overriding the default popover style on the sign up page.
One question I have: is it okay to override the popover style only on this page? /cc @mssola
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vitoravelino I don't think it's ok to override it. All popovers should look and feel in the same way throughout the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I thought. So, these styling can be removed then.
I didn't see any form validation using popover. It's using the built-in validators. I think you can use setCustomValidity method to achieve the same look and feel.
What do you think, @mssola?
} | ||
|
||
#signup_page .popover.right > .arrow:after{ | ||
border-right-color: rgba(0,0,0,0.6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing Now..:D
i.fa.fa-check | ||
- if @admin || !@first_user_admin | ||
| Create account | ||
- else | ||
| Create admin | ||
- if @have_users | ||
.text-center = link_to 'I already have an account. Login.', new_user_session_path, class: 'btn btn-link' | ||
javascript: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this out of the slim file.
I suggest you to follow the same structure done on #1233. Since I was responsible for that, I can help you if you need any help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I move the javascript to the Portus/app/assets/javascripts/modules/users/pages/sign-up.js ??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only move to that but also restructure the code itself to follow a more component based style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @vitoravelino. Please change this to a more component based style.
= f.email_field :email, class: 'form-control input-lg first', placeholder: 'Email', required: true | ||
= f.password_field :password, class: 'form-control input-lg', placeholder: 'Password (8 characters min.)', required: true | ||
= f.password_field :password_confirmation, class: 'form-control input-lg last', placeholder: 'Password again', required: true | ||
= f.text_field :username, class: 'form-control input-lg', placeholder: 'Username', required: true, autofocus: true,rel: 'popover' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a space after comma.
= f.password_field :password_confirmation, class: 'form-control input-lg last', placeholder: 'Password again', required: true | ||
= f.text_field :username, class: 'form-control input-lg', placeholder: 'Username', required: true, autofocus: true,rel: 'popover' | ||
= f.email_field :email, class: 'form-control input-lg first', placeholder: 'Email', required: true, rel: 'popover' | ||
= f.password_field :password, class: 'form-control input-lg', placeholder: 'Password (8 characters min.)', required: true,rel: 'popover' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
= f.text_field :username, class: 'form-control input-lg', placeholder: 'Username', required: true, autofocus: true,rel: 'popover' | ||
= f.email_field :email, class: 'form-control input-lg first', placeholder: 'Email', required: true, rel: 'popover' | ||
= f.password_field :password, class: 'form-control input-lg', placeholder: 'Password (8 characters min.)', required: true,rel: 'popover' | ||
= f.password_field :password_confirmation, class: 'form-control input-lg last', placeholder: 'Password again', required: true,rel: 'popover' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the submit button must be disabled at the starting. I will change the popover width too.
Popover width too thin. Submit button disabled if form is empty since is invalid. |
Tests are very welcome too. You can check the some of the rspec feature tests. 😄 |
I am implementing the changes.... will push them soon...! I will also try to make travis pass. |
Travis is already passing. That's a false negative, don't worry. It happens sometimes. 😅 |
@vitoravelino I did something similar to password-form.js by making signup-form.js and then using it from signup.js in pages . But when i check for event listener on the user_password and user_password_confirmation and email, Its not showing any. Please help me in this. I am new to this component based style. |
@krishnak9, could you push the code? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this 👏 Just address @vitoravelino's comments.
// interactions. | ||
class UsersSignUpForm extends BaseComponent { | ||
elements() { | ||
this.$Password = this.$el.find(PASSWORD_FIELD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you rename variable to lower case ? (e.g. this.$Password
should be this.$password
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes . I will do that for sure .
onKeyup() { | ||
const Password = this.$Password.val(); | ||
const PasswordConfirmation = this.$PasswordConfirmation.val(); | ||
const PasswordInvalid = !Password; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed.
mount() { | ||
fadeIn(this.$el); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't remove this 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
@@ -0,0 +1,12 @@ | |||
//popover css used in signup page for client side validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really have to re-style popovers ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Github hided my comment regarding this.
"That's what I thought. So, these styling can be removed then.
I didn't see any form validation using popover. It's using the built-in validators. I think you can use setCustomValidity method to achieve the same look and feel.
What do you think, @mssola?"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean its not much necessary but if we use popover for validation messages then the width of the popover must be increased. But i think i will remove this and use HTMLSelectElement.setCustomValidity() as suggested by @vitoravelino
Now you can remove all that jQuery code right ? |
} | ||
|
||
events() { | ||
this.$el.on('keyup', PASSWORD_FIELD, e => this.onKeyup(e)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the reason nothing is happening on the focus out is because is you listening to the wrong event. You should use blur
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also rename the method event listener.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even on the 'keyup' its not listening.
} | ||
|
||
onKeyup() { | ||
const Password = this.$Password.val(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lowercase for variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am on it :) .!
Yeah I will remove all that jquery codes on the slim file, but i still ididnt get this component based style structure to work. I am still doing it. I am new to this so might take some time to learn all. |
@krishnak9 Let me try to explain. There's a BaseComponent class that is a simple empty js class that tries to avoid some boilerplate of having to call common methods that repeats in every component. There's an order and purpose for those methods. When you extend Components should do as simple things as possible, if you have many responsibilities in a component, you might need to split into separated components or delegate to a service. Regarding migrating your initial code to this structure, you can see that you repeat yourself a lot wrapping the same dom elements, right? I guess you've already figured that out and started putting all the elements that you are gonna need to access inside the Second, and really important, thing: events. When you need to listen to any event from any dom element (ideally below (e) => this.onKeyup(e) is the same thing as function (e) {
this.onKeyUp(e);
}.bind(this) That's just some syntax suger from ES2015. This covers the basics of the current component based structure. With that knowledge I think you can do most of the migration. Let me know if you have any question. |
Finally found what was going wrong. I didn't reload my vagrant environment and thats why the changes didn't take place 😅. I also removed the popover and implemented |
const passwordInvalid = !this.$password.val() || this.$password.val().length < 8; | ||
const passwordConfirmationInvalid = !this.$passwordConfirmation.val() || | ||
this.$password.val() !== this.$passwordConfirmation.val(); | ||
if (passwordConfirmationInvalid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an empty line above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before the if
statement ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Makes code easier to read.
} else { | ||
this.$passwordConfirmation[0].setCustomValidity(''); | ||
} | ||
if (passwordInvalid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
this.$password[0].setCustomValidity(''); | ||
this.$passwordConfirmation.attr('pattern', this.$password.val()); | ||
} | ||
if (passwordInvalid || passwordConfirmationInvalid || emailInvalid || usernameInvalid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
this.$password = this.$el.find(PASSWORD_FIELD); | ||
this.$passwordConfirmation = this.$el.find(CONFIRMATION_PASSWORD_FIELD); | ||
this.$username = this.$el.find(USERNAME_FIELD); | ||
this.$userEmail = this.$el.find(EMAIL_FIELD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are not gonna use the $element, you don't need to cache it here. So, I think you can remove both username
and email
elements, constants and event listeners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. My bad! Forgot the disabled button. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah ..they are used in emailInvalid
, usernameInvalid
and for disabling the submit button. So no need to remove them right ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need. I just woke up. Wasn't thinking really straight yet. KKKK.
} | ||
|
||
onfocusout() { | ||
const usernameInvalid = !this.$username; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should do the same as you did for email, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah ! I will do that . In that way if we use any pattern based user-name ,it will also get validated.
this.$el.on('focusout', EMAIL_FIELD, e => this.onfocusout(e)); | ||
} | ||
|
||
onfocusout() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to rename it to onFocusout
. The idea (at least that's what I do) is to start a function/method with on
only if it's an event listener/callback. That will highlight that this is probably a a function that is used as an event listener/callback related to the focusout
event.
I know that in this case it wouldn't add much value because this component is very simple, but it's good to have consistency around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah..! Sure 😀.!
Did the changes! , Don't know why travis is failing here. In my fork it did passed Travis (commit: changes) |
It's passing, don't worry. As I said before, it's a false negative. |
Oh Cool 😀 . Any more changes ! |
this.$password.val() !== this.$passwordConfirmation.val(); | ||
|
||
if (passwordConfirmationInvalid) { | ||
this.$passwordConfirmation[0].setCustomValidity('Please enter same Password as Above'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To lowercase Password
and Above
.
And yeah, please add a feature test 👍 |
To write feature tests for this, I have to execute the javascript using capybara-webkit driver, therefore I need to include this gem too. As I have to trigger the |
There's no need to include any new gem. Notice that whenever you want a feature test to be able to execute JS, you need to add the |
Sorry I got busy with the hectic college schedule. For feature test I have done something like this
I can't find out what caused this error. @vitoravelino @mssola |
@krishnak9 iirc, that only worked (as in the other tests of this same file) when fill_in "Username", with: user.username or with something like this (I don't recall if this was the exact way of doing it though): find("#user_username").fill user.username You can take a look at the |
Oh okay 😃 !. I will try to find out the approach for this . Thanks..! I will check by using this |
Finally found a way to work through 😅. It's related with the page path, I had to define the path explicitly. Added the feature test for Client side validation. @mssola @vitoravelino Please review. 😃 |
page.execute_script "$('#user_username').trigger('focusout')" | ||
wait_for_effect_on("#user_username") | ||
expect(page).to have_button("submit_btn", disabled: true) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove empty line.
page.execute_script "$('#user_username').trigger('focusout')" | ||
wait_for_effect_on("#user_username") | ||
expect(page).to have_button("submit_btn", disabled: false) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
fill_in "user_password", with: "12341234" | ||
fill_in "user_password_confirmation", with: "532" | ||
page.execute_script "$('#user_username').trigger('focusout')" | ||
wait_for_effect_on("#user_username") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you test without this? Just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I didn't test without this. I just want to make sure that focusout
get's completed before the check for disabled submit button.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it as @mssola mentioned about test might become flaky 😅
You can take a look at the spec/features/namespaces_spec.rb file to check some tests using js: true. Moreover, I'd recommend adding a wait method before this: expect(page).to have_button('submit_btn', disabled: true). Otherwise this might become a flaky test I fear 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'd wait for the effect on the submit button, since this is the element that will change its state.
What more should be done here . Or is this concluded. Please review @mssola @vitoravelino . |
@krishnak9 fix my nitpick, and then squash your commits into a single one. Then I'd say it will be good to be merged. Thanks 👏 |
05617a2
to
5f90a00
Compare
Just one final thing, could you sign off your commit as specified in the CONTRIBUTING.md file ? If you have your author & email in your git config, it should be a matter of just using the Thanks! |
Signed-off-by: Krishna Kumar <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of considerations before merging (sorry for the trouble...):
- The submit button should be disabled when loading the page (this is as simple as adding the
disabled="true"
element in the HTML code. - This should be done on key press maybe instead of on focus out (maybe with some timer so we don't hit this code a gazillion times). This is because on a usual workflow, when I type the password confirmation in the end, I should see have the button enabled again without me focusing out the element.
What do you think ?
Oh okay. I will disable the button at the starting. I see the problem here. Is it okay that if I add a new |
You can even write a very simple feature test that checks that when visiting the signup page this element is disabled.
It has to be applied always. I just said password confirmation as an example. Another use case:
In this case, the button won't be enabled when I've just corrected. So, to be fair, this should be done for each input element. |
In order to make it more readable, I guess you could have individuals event listeners for each field and store the validation result in a variable. This way you are more flexible to manage the experience of giving a specific feedback back to the user based on the field. In the end of each of these listeners you would need to a common method that will check if all the validations are valid and if yes, enable the button, otherwise disable it. Since the project is not using a lib/fw with some feature/sugar syntax and |
Yeah this is true. This should be done on all the input elements .I will do the changes. I will try to do with individual event listeners or the timer based approach, I will check for it. Will push the changes soon. |
I have implemented this using |
@krishnak9 sorry the delay, any luck so far? |
@krishnak9 are you still working on this ? |
@mssola @vitoravelino Sorry Sir ! I got busy with my college campus placements so I m not getting time to actually work on this. If required you can close this PR. When I will resume the work, I will send a new PR. |
Let's close this then. It can be re-opened if none else has worked on this in the meantime. Thanks for working on this 👍 |
#861 This implements the client side validation to the signup page.
These are few screenshots:-
Submit button gets disabled when user enters an invalid entry.
The submit button gets enabled after all fields are correct.