Resolve "Inline validation for user's name and username length"
This commit is contained in:
parent
5caffe48a9
commit
c38ea7e884
|
|
@ -1,10 +1,11 @@
|
|||
import { __ } from '~/locale';
|
||||
import emojiRegex from 'emoji-regex';
|
||||
import InputValidator from '../validators/input_validator';
|
||||
|
||||
const invalidInputClass = 'gl-field-error-outline';
|
||||
|
||||
export default class NoEmojiValidator {
|
||||
export default class NoEmojiValidator extends InputValidator {
|
||||
constructor(opts = {}) {
|
||||
super();
|
||||
|
||||
const container = opts.container || '';
|
||||
this.noEmojiEmelents = document.querySelectorAll(`${container} .js-block-emoji`);
|
||||
|
||||
|
|
@ -19,45 +20,14 @@ export default class NoEmojiValidator {
|
|||
|
||||
const { value } = this.inputDomElement;
|
||||
|
||||
this.errorMessage = __('Invalid input, please avoid emojis');
|
||||
|
||||
this.validatePattern(value);
|
||||
this.setValidationStateAndMessage();
|
||||
}
|
||||
|
||||
validatePattern(value) {
|
||||
const pattern = emojiRegex();
|
||||
this.hasEmojis = new RegExp(pattern).test(value);
|
||||
|
||||
if (this.hasEmojis) {
|
||||
this.inputDomElement.setCustomValidity(__('Invalid input, please avoid emojis'));
|
||||
} else {
|
||||
this.inputDomElement.setCustomValidity('');
|
||||
}
|
||||
}
|
||||
|
||||
setValidationStateAndMessage() {
|
||||
if (!this.inputDomElement.checkValidity()) {
|
||||
this.setInvalidState();
|
||||
} else {
|
||||
this.clearFieldValidationState();
|
||||
}
|
||||
}
|
||||
|
||||
clearFieldValidationState() {
|
||||
this.inputDomElement.classList.remove(invalidInputClass);
|
||||
this.inputErrorMessage.classList.add('hide');
|
||||
}
|
||||
|
||||
setInvalidState() {
|
||||
this.inputDomElement.classList.add(invalidInputClass);
|
||||
this.setErrorMessage();
|
||||
}
|
||||
|
||||
setErrorMessage() {
|
||||
if (this.hasEmojis) {
|
||||
this.inputErrorMessage.innerHTML = this.inputDomElement.validationMessage;
|
||||
} else {
|
||||
this.inputErrorMessage.innerHTML = this.inputDomElement.title;
|
||||
}
|
||||
this.inputErrorMessage.classList.remove('hide');
|
||||
this.invalidInput = new RegExp(pattern).test(value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import $ from 'jquery';
|
||||
import LengthValidator from './length_validator';
|
||||
import UsernameValidator from './username_validator';
|
||||
import NoEmojiValidator from '../../../emoji/no_emoji_validator';
|
||||
import SigninTabsMemoizer from './signin_tabs_memoizer';
|
||||
|
|
@ -6,6 +7,7 @@ import OAuthRememberMe from './oauth_remember_me';
|
|||
import preserveUrlFragment from './preserve_url_fragment';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new LengthValidator(); // eslint-disable-line no-new
|
||||
new UsernameValidator(); // eslint-disable-line no-new
|
||||
new SigninTabsMemoizer(); // eslint-disable-line no-new
|
||||
new NoEmojiValidator(); // eslint-disable-line no-new
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import InputValidator from '../../../validators/input_validator';
|
||||
|
||||
const errorMessageClass = 'gl-field-error';
|
||||
|
||||
export default class LengthValidator extends InputValidator {
|
||||
constructor(opts = {}) {
|
||||
super();
|
||||
|
||||
const container = opts.container || '';
|
||||
const validateLengthElements = document.querySelectorAll(`${container} .js-validate-length`);
|
||||
|
||||
validateLengthElements.forEach(element =>
|
||||
element.addEventListener('input', this.eventHandler.bind(this)),
|
||||
);
|
||||
}
|
||||
|
||||
eventHandler(event) {
|
||||
this.inputDomElement = event.target;
|
||||
this.inputErrorMessage = this.inputDomElement.parentElement.querySelector(
|
||||
`.${errorMessageClass}`,
|
||||
);
|
||||
|
||||
const { value } = this.inputDomElement;
|
||||
const { maxLengthMessage, maxLength } = this.inputDomElement.dataset;
|
||||
|
||||
this.errorMessage = maxLengthMessage;
|
||||
|
||||
this.invalidInput = value.length > parseInt(maxLength, 10);
|
||||
|
||||
this.setValidationStateAndMessage();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
const invalidInputClass = 'gl-field-error-outline';
|
||||
|
||||
export default class InputValidator {
|
||||
constructor() {
|
||||
this.inputDomElement = {};
|
||||
this.inputErrorMessage = {};
|
||||
this.errorMessage = null;
|
||||
this.invalidInput = null;
|
||||
}
|
||||
|
||||
setValidationStateAndMessage() {
|
||||
this.setValidationMessage();
|
||||
|
||||
const isInvalidInput = !this.inputDomElement.checkValidity();
|
||||
this.inputDomElement.classList.toggle(invalidInputClass, isInvalidInput);
|
||||
this.inputErrorMessage.classList.toggle('hide', !isInvalidInput);
|
||||
}
|
||||
|
||||
setValidationMessage() {
|
||||
if (this.invalidInput) {
|
||||
this.inputDomElement.setCustomValidity(this.errorMessage);
|
||||
this.inputErrorMessage.innerHTML = this.errorMessage;
|
||||
} else {
|
||||
this.resetValidationMessage();
|
||||
}
|
||||
}
|
||||
|
||||
resetValidationMessage() {
|
||||
if (this.inputDomElement.validationMessage === this.errorMessage) {
|
||||
this.inputDomElement.setCustomValidity('');
|
||||
this.inputErrorMessage.innerHTML = this.inputDomElement.title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,7 +73,8 @@
|
|||
.login-body {
|
||||
font-size: 13px;
|
||||
|
||||
input + p {
|
||||
input + p,
|
||||
input ~ p.field-validation {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
- max_name_length = 128
|
||||
- max_username_length = 255
|
||||
#register-pane.tab-pane.login-box{ role: 'tabpanel' }
|
||||
.login-body
|
||||
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
|
||||
|
|
@ -5,13 +7,13 @@
|
|||
= render "devise/shared/error_messages", resource: resource
|
||||
.name.form-group
|
||||
= f.label :name, _('Full name'), class: 'label-bold'
|
||||
= f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji", required: true, title: _("This field is required.")
|
||||
= f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length } }, required: true, title: _("This field is required.")
|
||||
.username.form-group
|
||||
= f.label :username, class: 'label-bold'
|
||||
= f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
|
||||
%p.validation-error.hide= _('Username is already taken.')
|
||||
%p.validation-success.hide= _('Username is available.')
|
||||
%p.validation-pending.hide= _('Checking username availability...')
|
||||
= f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji js-validate-length", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length } }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
|
||||
%p.validation-error.field-validation.hide= _('Username is already taken.')
|
||||
%p.validation-success.field-validation.hide= _('Username is available.')
|
||||
%p.validation-pending.field-validation.hide= _('Checking username availability...')
|
||||
.form-group
|
||||
= f.label :email, class: 'label-bold'
|
||||
= f.email_field :email, class: "form-control middle qa-new-user-email", required: true, title: _("Please provide a valid email address.")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update registration form to indicate invalid name or username length on input
|
||||
merge_request: 28095
|
||||
author: Jiaan Louw
|
||||
type: changed
|
||||
|
|
@ -9130,6 +9130,12 @@ msgstr ""
|
|||
msgid "Sign-up restrictions"
|
||||
msgstr ""
|
||||
|
||||
msgid "SignUp|Name is too long (maximum is %{max_length} characters)."
|
||||
msgstr ""
|
||||
|
||||
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Signed in"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ describe 'Signup' do
|
|||
expect(find('.username')).not_to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'does not show an error border if the username length is not longer than 255 characters' do
|
||||
fill_in 'new_user_username', with: 'u' * 255
|
||||
wait_for_requests
|
||||
|
||||
expect(find('.username')).not_to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error border if the username already exists' do
|
||||
existing_user = create(:user)
|
||||
|
||||
|
|
@ -41,6 +48,20 @@ describe 'Signup' do
|
|||
expect(find('.username')).to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error border if the username is longer than 255 characters' do
|
||||
fill_in 'new_user_username', with: 'u' * 256
|
||||
wait_for_requests
|
||||
|
||||
expect(find('.username')).to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error message if the username is longer than 255 characters' do
|
||||
fill_in 'new_user_username', with: 'u' * 256
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content("Username is too long (maximum is 255 characters).")
|
||||
end
|
||||
|
||||
it 'shows an error message on submit if the username contains special characters' do
|
||||
fill_in 'new_user_username', with: 'new$user!username'
|
||||
wait_for_requests
|
||||
|
|
@ -67,14 +88,35 @@ describe 'Signup' do
|
|||
before do
|
||||
visit root_path
|
||||
click_link 'Register'
|
||||
simulate_input('#new_user_name', 'Ehsan 🦋')
|
||||
end
|
||||
|
||||
it 'does not show an error border if the user\'s fullname length is not longer than 128 characters' do
|
||||
fill_in 'new_user_name', with: 'u' * 128
|
||||
|
||||
expect(find('.name')).not_to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error border if the user\'s fullname contains an emoji' do
|
||||
simulate_input('#new_user_name', 'Ehsan 🦋')
|
||||
|
||||
expect(find('.name')).to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error border if the user\'s fullname is longer than 128 characters' do
|
||||
fill_in 'new_user_name', with: 'n' * 129
|
||||
|
||||
expect(find('.name')).to have_css '.gl-field-error-outline'
|
||||
end
|
||||
|
||||
it 'shows an error message if the user\'s fullname is longer than 128 characters' do
|
||||
fill_in 'new_user_name', with: 'n' * 129
|
||||
|
||||
expect(page).to have_content("Name is too long (maximum is 128 characters).")
|
||||
end
|
||||
|
||||
it 'shows an error message if the username contains emojis' do
|
||||
simulate_input('#new_user_name', 'Ehsan 🦋')
|
||||
|
||||
expect(page).to have_content("Invalid input, please avoid emojis")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue