diff options
-rw-r--r-- | app/assets/javascripts/emoji/no_emoji_validator.js | 63 | ||||
-rw-r--r-- | app/assets/javascripts/pages/sessions/new/index.js | 2 | ||||
-rw-r--r-- | app/views/devise/shared/_signup_box.html.haml | 6 | ||||
-rw-r--r-- | changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml | 5 | ||||
-rw-r--r-- | locale/gitlab.pot | 9 | ||||
-rw-r--r-- | spec/features/users/signup_spec.rb | 28 |
6 files changed, 110 insertions, 3 deletions
diff --git a/app/assets/javascripts/emoji/no_emoji_validator.js b/app/assets/javascripts/emoji/no_emoji_validator.js new file mode 100644 index 00000000000..0fd4dd74953 --- /dev/null +++ b/app/assets/javascripts/emoji/no_emoji_validator.js @@ -0,0 +1,63 @@ +import { __ } from '~/locale'; +import emojiRegex from 'emoji-regex'; + +const invalidInputClass = 'gl-field-error-outline'; + +export default class NoEmojiValidator { + constructor(opts = {}) { + const container = opts.container || ''; + this.noEmojiEmelents = document.querySelectorAll(`${container} .js-block-emoji`); + + this.noEmojiEmelents.forEach(element => + element.addEventListener('input', this.eventHandler.bind(this)), + ); + } + + eventHandler(event) { + this.inputDomElement = event.target; + this.inputErrorMessage = this.inputDomElement.nextSibling; + + const { value } = this.inputDomElement; + + 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'); + } +} diff --git a/app/assets/javascripts/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js index d54bff88f70..e1a3f42a71f 100644 --- a/app/assets/javascripts/pages/sessions/new/index.js +++ b/app/assets/javascripts/pages/sessions/new/index.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import UsernameValidator from './username_validator'; +import NoEmojiValidator from '../../../emoji/no_emoji_validator'; import SigninTabsMemoizer from './signin_tabs_memoizer'; import OAuthRememberMe from './oauth_remember_me'; import preserveUrlFragment from './preserve_url_fragment'; @@ -7,6 +8,7 @@ import preserveUrlFragment from './preserve_url_fragment'; document.addEventListener('DOMContentLoaded', () => { new UsernameValidator(); // eslint-disable-line no-new new SigninTabsMemoizer(); // eslint-disable-line no-new + new NoEmojiValidator(); // eslint-disable-line no-new new OAuthRememberMe({ container: $('.omniauth-container'), diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 004a3528d4b..9c7ca6ebbd4 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -3,12 +3,12 @@ = 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| .devise-errors = devise_error_messages! - .form-group + .name.form-group = f.label :name, 'Full name', class: 'label-bold' - = f.text_field :name, class: "form-control top qa-new-user-name", required: true, title: "This field is required." + = f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji", 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", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: 'Please create a username with only alphanumeric characters.' + = 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... diff --git a/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml b/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml new file mode 100644 index 00000000000..cc3a60479d3 --- /dev/null +++ b/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml @@ -0,0 +1,5 @@ +--- +title: Add realtime validation for user fullname and username on validation +merge_request: 25017 +author: Ehsan Abdulqader @EhsanZ +type: added diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5ac9d7c8bd4..9747c1ee952 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4058,6 +4058,9 @@ msgstr "" msgid "Introducing Your Conversational Development Index" msgstr "" +msgid "Invalid input, please avoid emojis" +msgstr "" + msgid "Invitation" msgstr "" @@ -5470,6 +5473,9 @@ msgstr "" msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again." msgstr "" +msgid "Please create a username with only alphanumeric characters." +msgstr "" + msgid "Please fill in a descriptive name for your group." msgstr "" @@ -7387,6 +7393,9 @@ msgstr "" msgid "This domain is not verified. You will need to verify ownership before access is enabled." msgstr "" +msgid "This field is required." +msgstr "" + msgid "This group" msgstr "" diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index bfe11ddf673..957c3cfc583 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -49,6 +49,34 @@ describe 'Signup' do expect(page).to have_content("Please create a username with only alphanumeric characters.") end + + it 'shows an error border if the username contains emojis' do + simulate_input('#new_user_username', 'ehsan😀') + + expect(find('.username')).to have_css '.gl-field-error-outline' + end + + it 'shows an error message if the username contains emojis' do + simulate_input('#new_user_username', 'ehsan😀') + + expect(page).to have_content("Invalid input, please avoid emojis") + end + end + + describe 'user\'s full name validation', :js do + before do + visit root_path + click_link 'Register' + simulate_input('#new_user_name', 'Ehsan 🦋') + end + + it 'shows an error border if the user\'s fullname contains an emoji' do + expect(find('.name')).to have_css '.gl-field-error-outline' + end + + it 'shows an error message if the username contains emojis' do + expect(page).to have_content("Invalid input, please avoid emojis") + end end context 'with no errors' do |