Fixed submit-on-enter and password manager issues on signup

closes https://github.com/TryGhost/Ghost/issues/9868
- moved submit action to the `<form>` which allowed removal of the per-input enter key handlers
- changed submit button to trigger the form submit
- improved markup for password managers
  - linked labels with inputs
  - changed button to a "submit" type and linked it with the form
  - added hidden email input at the bottom of the form in case password managers do not take disabled inputs into account
This commit is contained in:
Kevin Ansfield 2018-09-17 16:03:58 +01:00
parent ee36284440
commit 525fb2a720
6 changed files with 34 additions and 21 deletions

View File

@ -24,7 +24,7 @@ const GhTaskButton = Component.extend({
'isSuccessClass',
'isFailureClass'
],
attributeBindings: ['disabled', 'type', 'tabindex'],
attributeBindings: ['disabled', 'form', 'type', 'tabindex'],
task: null,
disabled: false,

View File

@ -1,6 +1,5 @@
import Controller from '@ember/controller';
import RSVP from 'rsvp';
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import {
VersionMismatchError,
isVersionMismatchError
@ -10,7 +9,7 @@ import {isArray as isEmberArray} from '@ember/array';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
export default Controller.extend(ValidationEngine, {
export default Controller.extend({
ajax: service(),
config: service(),
ghostPaths: service(),
@ -21,13 +20,11 @@ export default Controller.extend(ValidationEngine, {
flowErrors: '',
profileImage: null,
// ValidationEngine settings
validationType: 'signup',
signupDetails: alias('model'),
actions: {
signup() {
this.get('signup').perform();
validate(property) {
return this.signupDetails.validate({property});
},
setImage(image) {
@ -84,10 +81,10 @@ export default Controller.extend(ValidationEngine, {
let notifications = this.get('notifications');
this.set('flowErrors', '');
this.get('hasValidated').addObjects(setupProperties);
this.get('signupDetails.hasValidated').addObjects(setupProperties);
try {
yield this.validate();
yield this.signupDetails.validate();
yield this._completeInvitation();
try {
@ -100,6 +97,7 @@ export default Controller.extend(ValidationEngine, {
// ValidationEngine throws undefined
if (!error) {
this.set('flowErrors', 'Please fill out the form to complete your sign-up');
return false;
}
if (error && error.payload && error.payload.errors && isEmberArray(error.payload.errors)) {
@ -111,7 +109,7 @@ export default Controller.extend(ValidationEngine, {
notifications.showAPIError(error, {key: 'signup.complete'});
}
}
}),
}).drop(),
_completeInvitation() {
let authUrl = this.get('ghostPaths.url').api('authentication', 'invitation');

View File

@ -3,6 +3,7 @@ import EmberObject from '@ember/object';
import RSVP from 'rsvp';
import Route from '@ember/routing/route';
import UnauthenticatedRouteMixin from 'ghost-admin/mixins/unauthenticated-route-mixin';
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import styleBody from 'ghost-admin/mixins/style-body';
import {inject as service} from '@ember/service';
@ -27,7 +28,10 @@ export default Route.extend(styleBody, UnauthenticatedRouteMixin, {
},
model(params) {
let signupDetails = EmberObject.create();
let SignupDetails = EmberObject.extend(ValidationEngine, {
validationType: 'signup'
});
let signupDetails = SignupDetails.create();
let re = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2}|[A-Za-z0-9_-]{3})?$/;
let email,
tokenText;

View File

@ -16,6 +16,7 @@
{{gh-trim-focus-input
tabindex="1"
type="text"
id="blog-title"
name="blog-title"
placeholder="Eg. The Daily Awesome"
autocorrect="off"
@ -33,6 +34,7 @@
{{svg-jar "user-circle"}}
{{gh-text-input
tabindex="2"
id="name"
name="name"
placeholder="Eg. John H. Watson"
autocorrect="off"
@ -51,6 +53,7 @@
{{gh-text-input
tabindex="3"
type="email"
id="email"
name="email"
placeholder="Eg. john@example.com"
autocorrect="off"
@ -69,6 +72,7 @@
{{gh-text-input
tabindex="4"
type="password"
id="password"
name="password"
placeholder="At least 10 characters"
autocorrect="off"

View File

@ -6,19 +6,20 @@
<h1>Create your account</h1>
</header>
<form id="signup" class="gh-flow-create" method="post" novalidate="novalidate">
<form id="signup" class="gh-flow-create" method="post" novalidate="novalidate" {{action (perform submit) on="submit"}}>
{{!-- Hack to stop Chrome's broken auto-fills --}}
<input style="display:none;" type="text" name="fakeusernameremembered"/>
<input style="display:none;" type="password" name="fakepasswordremembered"/>
{{gh-profile-image email=signupDetails.email setImage=(action "setImage")}}
{{#gh-form-group errors=signupDetails.errors hasValidated=hasValidated property="email"}}
<label for="email-address">Email address</label>
{{#gh-form-group errors=signupDetails.errors hasValidated=signupDetails.hasValidated property="email"}}
<label for="email">Email address</label>
<span class="gh-input-icon gh-icon-mail">
{{svg-jar "email"}}
{{gh-text-input
type="email"
id="email"
name="email"
placeholder="Eg. john@example.com"
disabled="disabled"
@ -29,37 +30,35 @@
</span>
{{/gh-form-group}}
{{#gh-form-group errors=signupDetails.errors hasValidated=hasValidated property="name"}}
<label for="full-name">Full name</label>
{{#gh-form-group errors=signupDetails.errors hasValidated=signupDetails.hasValidated property="name"}}
<label for="name">Full name</label>
<span class="gh-input-icon gh-icon-user">
{{svg-jar "user-circle"}}
{{gh-trim-focus-input
tabindex="1"
type="text"
id="name"
name="name"
placeholder="Eg. John H. Watson"
autocorrect="off"
value=(readonly signupDetails.name)
input=(action (mut signupDetails.name) value="target.value")
keyEvents=(hash
Enter=(action "signup")
)
focus-out=(action "validate" "name")
}}
</span>
{{gh-error-message errors=signupDetails.errors property="name"}}
{{/gh-form-group}}
{{#gh-form-group errors=signupDetails.errors hasValidated=hasValidated property="password"}}
{{#gh-form-group errors=signupDetails.errors hasValidated=signupDetails.hasValidated property="password"}}
<label for="password">Password</label>
<span class="gh-input-icon gh-icon-lock">
{{svg-jar "lock"}}
{{gh-text-input
tabindex="2"
type="password"
id="password"
name="password"
placeholder="At least 10 characters"
onenter=(action "signup")
autocorrect="off"
value=(readonly signupDetails.password)
input=(action (mut signupDetails.password) value="target.value")
@ -67,9 +66,14 @@
</span>
{{gh-error-message errors=signupDetails.errors property="password"}}
{{/gh-form-group}}
{{!-- include the email field again in case password managers ignore the disabled input --}}
<input type="hidden" name="email" value={{signupDetails.email}} />
</form>
{{gh-task-button "Create Account"
type="submit"
form="signup"
runningText="Creating"
task=signup
class="gh-btn gh-btn-green gh-btn-lg gh-btn-block gh-btn-icon"

View File

@ -14,6 +14,7 @@ export default BaseValidator.extend(PasswordValidatorMixin, {
if (!validator.isLength(name || '', 1)) {
model.get('errors').add('name', 'Please enter a name.');
model.get('hasValidated').addObject('email');
this.invalidate();
}
},
@ -28,6 +29,8 @@ export default BaseValidator.extend(PasswordValidatorMixin, {
model.get('errors').add('email', 'Invalid Email.');
this.invalidate();
}
model.get('hasValidated').addObject('email');
},
password(model) {