From f1b82363eda7fb47a4272fd5b72d656330a6df92 Mon Sep 17 00:00:00 2001
From: Paul Slaughter <pslaughter@gitlab.com>
Date: Wed, 17 Mar 2021 14:27:08 -0500
Subject: [PATCH] Refactor UserInternalRegexHandler to own module

- Also removes jquery usage
---
 app/assets/javascripts/admin/users/new.js     | 55 ++++++++++++++
 .../pages/admin/users/new/index.js            | 50 +-----------
 ...s-refactor-user-internal-regex-handler.yml |  5 ++
 spec/frontend/admin/users/new_spec.js         | 76 +++++++++++++++++++
 .../pages/admin/users/new/index_spec.js       | 41 ----------
 5 files changed, 138 insertions(+), 89 deletions(-)
 create mode 100644 app/assets/javascripts/admin/users/new.js
 create mode 100644 changelogs/unreleased/ps-refactor-user-internal-regex-handler.yml
 create mode 100644 spec/frontend/admin/users/new_spec.js
 delete mode 100644 spec/frontend/pages/admin/users/new/index_spec.js

diff --git a/app/assets/javascripts/admin/users/new.js b/app/assets/javascripts/admin/users/new.js
new file mode 100644
index 00000000000..33565bfc14f
--- /dev/null
+++ b/app/assets/javascripts/admin/users/new.js
@@ -0,0 +1,55 @@
+const DATA_ATTR_REGEX_PATTERN = 'data-user-internal-regex-pattern';
+const DATA_ATTR_REGEX_OPTIONS = 'data-user-internal-regex-options';
+export const ID_USER_EXTERNAL = 'user_external';
+export const ID_WARNING = 'warning_external_automatically_set';
+export const ID_USER_EMAIL = 'user_email';
+
+const getAttributeValue = (attr) => document.querySelector(`[${attr}]`)?.getAttribute(attr);
+
+const getRegexPattern = () => getAttributeValue(DATA_ATTR_REGEX_PATTERN);
+
+const getRegexOptions = () => getAttributeValue(DATA_ATTR_REGEX_OPTIONS);
+
+export const setupInternalUserRegexHandler = () => {
+  const regexPattern = getRegexPattern();
+
+  if (!regexPattern) {
+    return;
+  }
+
+  const regexOptions = getRegexOptions();
+  const elExternal = document.getElementById(ID_USER_EXTERNAL);
+  const elWarningMessage = document.getElementById(ID_WARNING);
+  const elUserEmail = document.getElementById(ID_USER_EMAIL);
+
+  const isEmailInternal = (email) => {
+    const regex = new RegExp(regexPattern, regexOptions);
+    return regex.test(email);
+  };
+
+  const setExternalCheckbox = (email) => {
+    const isChecked = elExternal.checked;
+
+    if (isEmailInternal(email)) {
+      if (isChecked) {
+        elExternal.checked = false;
+        elWarningMessage.classList.remove('hidden');
+      }
+    } else if (!isChecked) {
+      elExternal.checked = true;
+      elWarningMessage.classList.add('hidden');
+    }
+  };
+
+  const setupListeners = () => {
+    elUserEmail.addEventListener('input', (event) => {
+      setExternalCheckbox(event.target.value);
+    });
+
+    elExternal.addEventListener('change', () => {
+      elWarningMessage.classList.add('hidden');
+    });
+  };
+
+  setupListeners();
+};
diff --git a/app/assets/javascripts/pages/admin/users/new/index.js b/app/assets/javascripts/pages/admin/users/new/index.js
index 01710246c86..34c10e44f4c 100644
--- a/app/assets/javascripts/pages/admin/users/new/index.js
+++ b/app/assets/javascripts/pages/admin/users/new/index.js
@@ -1,49 +1,3 @@
-import $ from 'jquery';
+import { setupInternalUserRegexHandler } from '~/admin/users/new';
 
-export default class UserInternalRegexHandler {
-  constructor() {
-    this.regexPattern = $('[data-user-internal-regex-pattern]').data('user-internal-regex-pattern');
-    if (this.regexPattern && this.regexPattern !== '') {
-      this.regexOptions = $('[data-user-internal-regex-options]').data(
-        'user-internal-regex-options',
-      );
-      this.external = $('#user_external');
-      this.warningMessage = $('#warning_external_automatically_set');
-      this.addListenerToEmailField();
-      this.addListenerToUserExternalCheckbox();
-    }
-  }
-
-  addListenerToEmailField() {
-    $('#user_email').on('input', (event) => {
-      this.setExternalCheckbox(event.currentTarget.value);
-    });
-  }
-
-  addListenerToUserExternalCheckbox() {
-    this.external.on('click', () => {
-      this.warningMessage.addClass('hidden');
-    });
-  }
-
-  isEmailInternal(email) {
-    const regex = new RegExp(this.regexPattern, this.regexOptions);
-    return regex.test(email);
-  }
-
-  setExternalCheckbox(email) {
-    const isChecked = this.external.prop('checked');
-    if (this.isEmailInternal(email)) {
-      if (isChecked) {
-        this.external.prop('checked', false);
-        this.warningMessage.removeClass('hidden');
-      }
-    } else if (!isChecked) {
-      this.external.prop('checked', true);
-      this.warningMessage.addClass('hidden');
-    }
-  }
-}
-
-// eslint-disable-next-line no-new
-new UserInternalRegexHandler();
+setupInternalUserRegexHandler();
diff --git a/changelogs/unreleased/ps-refactor-user-internal-regex-handler.yml b/changelogs/unreleased/ps-refactor-user-internal-regex-handler.yml
new file mode 100644
index 00000000000..156861328e1
--- /dev/null
+++ b/changelogs/unreleased/ps-refactor-user-internal-regex-handler.yml
@@ -0,0 +1,5 @@
+---
+title: In admin new user page, fix external checkbox warning hide with keyboard interaction
+merge_request: 56896
+author:
+type: fixed
diff --git a/spec/frontend/admin/users/new_spec.js b/spec/frontend/admin/users/new_spec.js
new file mode 100644
index 00000000000..692c583dca8
--- /dev/null
+++ b/spec/frontend/admin/users/new_spec.js
@@ -0,0 +1,76 @@
+import {
+  setupInternalUserRegexHandler,
+  ID_USER_EMAIL,
+  ID_USER_EXTERNAL,
+  ID_WARNING,
+} from '~/admin/users/new';
+
+describe('admin/users/new', () => {
+  const FIXTURE = 'admin/users/new_with_internal_user_regex.html';
+
+  let elExternal;
+  let elUserEmail;
+  let elWarningMessage;
+
+  beforeEach(() => {
+    loadFixtures(FIXTURE);
+    setupInternalUserRegexHandler();
+
+    elExternal = document.getElementById(ID_USER_EXTERNAL);
+    elUserEmail = document.getElementById(ID_USER_EMAIL);
+    elWarningMessage = document.getElementById(ID_WARNING);
+
+    elExternal.checked = true;
+  });
+
+  const changeEmail = (val) => {
+    elUserEmail.value = val;
+    elUserEmail.dispatchEvent(new Event('input'));
+  };
+
+  const hasHiddenWarning = () => elWarningMessage.classList.contains('hidden');
+
+  describe('Behaviour of userExternal checkbox', () => {
+    it('hides warning by default', () => {
+      expect(hasHiddenWarning()).toBe(true);
+    });
+
+    describe('when matches email as internal', () => {
+      beforeEach(() => {
+        changeEmail('test@');
+      });
+
+      it('has external unchecked', () => {
+        expect(elExternal.checked).toBe(false);
+      });
+
+      it('shows warning', () => {
+        expect(hasHiddenWarning()).toBe(false);
+      });
+
+      describe('when external is checked again', () => {
+        beforeEach(() => {
+          elExternal.dispatchEvent(new Event('change'));
+        });
+
+        it('hides warning', () => {
+          expect(hasHiddenWarning()).toBe(true);
+        });
+      });
+    });
+
+    describe('when matches emails as external', () => {
+      beforeEach(() => {
+        changeEmail('test.ext@');
+      });
+
+      it('has external checked', () => {
+        expect(elExternal.checked).toBe(true);
+      });
+
+      it('hides warning', () => {
+        expect(hasHiddenWarning()).toBe(true);
+      });
+    });
+  });
+});
diff --git a/spec/frontend/pages/admin/users/new/index_spec.js b/spec/frontend/pages/admin/users/new/index_spec.js
deleted file mode 100644
index ec9fe487030..00000000000
--- a/spec/frontend/pages/admin/users/new/index_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import $ from 'jquery';
-import UserInternalRegexHandler from '~/pages/admin/users/new/index';
-
-describe('UserInternalRegexHandler', () => {
-  const FIXTURE = 'admin/users/new_with_internal_user_regex.html';
-  let $userExternal;
-  let $userEmail;
-  let $warningMessage;
-
-  beforeEach(() => {
-    loadFixtures(FIXTURE);
-    // eslint-disable-next-line no-new
-    new UserInternalRegexHandler();
-    $userExternal = $('#user_external');
-    $userEmail = $('#user_email');
-    $warningMessage = $('#warning_external_automatically_set');
-    if (!$userExternal.prop('checked')) $userExternal.prop('checked', 'checked');
-  });
-
-  describe('Behaviour of userExternal checkbox when', () => {
-    it('matches email as internal', (done) => {
-      expect($warningMessage.hasClass('hidden')).toBeTruthy();
-
-      $userEmail.val('test@').trigger('input');
-
-      expect($userExternal.prop('checked')).toBeFalsy();
-      expect($warningMessage.hasClass('hidden')).toBeFalsy();
-      done();
-    });
-
-    it('matches email as external', (done) => {
-      expect($warningMessage.hasClass('hidden')).toBeTruthy();
-
-      $userEmail.val('test.ext@').trigger('input');
-
-      expect($userExternal.prop('checked')).toBeTruthy();
-      expect($warningMessage.hasClass('hidden')).toBeTruthy();
-      done();
-    });
-  });
-});
-- 
2.30.9