From 2e89eb6a494fa44d254939ea22c20cc557105ee2 Mon Sep 17 00:00:00 2001
From: Winnie Hellmann <winnie@gitlab.com>
Date: Tue, 16 May 2017 10:32:53 +0000
Subject: [PATCH] Replace broken autocomplete field for new tags with dropdown

---
 app/assets/javascripts/dispatcher.js          |  2 +
 app/assets/javascripts/new_branch_form.js     | 47 ++-----------------
 app/assets/javascripts/ref_select_dropdown.js | 46 ++++++++++++++++++
 app/views/projects/tags/new.html.haml         | 19 ++++----
 spec/features/tags/master_creates_tag_spec.rb | 16 ++++++-
 5 files changed, 76 insertions(+), 54 deletions(-)
 create mode 100644 app/assets/javascripts/ref_select_dropdown.js

diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 1a791395d6f..22d01c484d3 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -52,6 +52,7 @@ import Pipelines from './pipelines';
 import BlobViewer from './blob/viewer/index';
 import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
 import UsersSelect from './users_select';
+import RefSelectDropdown from './ref_select_dropdown';
 
 const ShortcutsBlob = require('./shortcuts_blob');
 
@@ -212,6 +213,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
         case 'projects:tags:new':
           new ZenMode();
           new gl.GLForm($('.tag-form'));
+          new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
           break;
         case 'projects:releases:edit':
           new ZenMode();
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 9d614cdee3a..39fb302b644 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,4 +1,6 @@
 /* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */
+import RefSelectDropdown from '~/ref_select_dropdown';
+
 (function() {
   this.NewBranchForm = (function() {
     function NewBranchForm(form, availableRefs) {
@@ -6,7 +8,7 @@
       this.branchNameError = form.find('.js-branch-name-error');
       this.name = form.find('.js-branch-name');
       this.ref = form.find('#ref');
-      this.setupAvailableRefs(availableRefs);
+      new RefSelectDropdown($('.js-branch-select'), availableRefs); // eslint-disable-line no-new
       this.setupRestrictions();
       this.addBinding();
       this.init();
@@ -22,49 +24,6 @@
       }
     };
 
-    NewBranchForm.prototype.setupAvailableRefs = function(availableRefs) {
-      var $branchSelect = $('.js-branch-select');
-
-      $branchSelect.glDropdown({
-        data: availableRefs,
-        filterable: true,
-        filterByText: true,
-        remote: false,
-        fieldName: $branchSelect.data('field-name'),
-        filterInput: 'input[type="search"]',
-        selectable: true,
-        isSelectable: function(branch, $el) {
-          return !$el.hasClass('is-active');
-        },
-        text: function(branch) {
-          return branch;
-        },
-        id: function(branch) {
-          return branch;
-        },
-        toggleLabel: function(branch) {
-          if (branch) {
-            return branch;
-          }
-        }
-      });
-
-      const $dropdownContainer = $branchSelect.closest('.dropdown');
-      const $fieldInput = $(`input[name="${$branchSelect.data('field-name')}"]`, $dropdownContainer);
-      const $filterInput = $('input[type="search"]', $dropdownContainer);
-
-      $filterInput.on('keyup', (e) => {
-        const keyCode = e.keyCode || e.which;
-        if (keyCode !== 13) return;
-
-        const text = $filterInput.val();
-        $fieldInput.val(text);
-        $('.dropdown-toggle-text', $branchSelect).text(text);
-
-        $dropdownContainer.removeClass('open');
-      });
-    };
-
     NewBranchForm.prototype.setupRestrictions = function() {
       var endsWith, invalid, single, startsWith;
       startsWith = {
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
new file mode 100644
index 00000000000..215cd6fbdfd
--- /dev/null
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -0,0 +1,46 @@
+class RefSelectDropdown {
+  constructor($dropdownButton, availableRefs) {
+    $dropdownButton.glDropdown({
+      data: availableRefs,
+      filterable: true,
+      filterByText: true,
+      remote: false,
+      fieldName: $dropdownButton.data('field-name'),
+      filterInput: 'input[type="search"]',
+      selectable: true,
+      isSelectable(branch, $el) {
+        return !$el.hasClass('is-active');
+      },
+      text(branch) {
+        return branch;
+      },
+      id(branch) {
+        return branch;
+      },
+      toggleLabel(branch) {
+        return branch;
+      },
+    });
+
+    const $dropdownContainer = $dropdownButton.closest('.dropdown');
+    const $fieldInput = $(`input[name="${$dropdownButton.data('field-name')}"]`, $dropdownContainer);
+    const $filterInput = $('input[type="search"]', $dropdownContainer);
+
+    $filterInput.on('keyup', (e) => {
+      const keyCode = e.keyCode || e.which;
+      if (keyCode !== 13) return;
+
+      const ref = $filterInput.val().trim();
+      if (ref === '') {
+        return;
+      }
+
+      $fieldInput.val(ref);
+      $('.dropdown-toggle-text', $dropdownButton).text(ref);
+
+      $dropdownContainer.removeClass('open');
+    });
+  }
+}
+
+export default RefSelectDropdown;
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index cbf841762b7..52af295bddd 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -1,4 +1,5 @@
 - page_title "New Tag"
+- default_ref = params[:ref] || @project.default_branch
 
 - if @error
   .alert.alert-danger
@@ -16,9 +17,13 @@
       = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
   .form-group
     = label_tag :ref, 'Create from', class: 'control-label'
-    .col-sm-10
-      = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control'
-      .help-block  Branch name or commit SHA
+    .col-sm-10.create-from
+      .dropdown
+        = hidden_field_tag :ref, default_ref
+        = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide form-control js-branch-select', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
+          .text-left.dropdown-toggle-text= default_ref
+        = render 'shared/ref_dropdown', dropdown_class: 'wide'
+      .help-block Existing branch name, tag, or commit SHA
   .form-group
     = label_tag :message, nil, class: 'control-label'
     .col-sm-10
@@ -37,9 +42,5 @@
     = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
 
 :javascript
-  var availableRefs = #{@project.repository.ref_names.to_json};
-
-  $("#ref").autocomplete({
-    source: availableRefs,
-    minLength: 1
-  });
+  window.gl = window.gl || { };
+  window.gl.availableRefs = #{@project.repository.ref_names.to_json};
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index ca25c696f75..af25eebed13 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -51,10 +51,24 @@ feature 'Master creates tag', feature: true do
     end
   end
 
+  scenario 'opens dropdown for ref', js: true do
+    click_link 'New tag'
+    ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
+    page.within ref_row do
+      ref_input = find('[name="ref"]', visible: false)
+      expect(ref_input.value).to eq 'master'
+      expect(find('.dropdown-toggle-text')).to have_content 'master'
+
+      find('.js-branch-select').trigger('click')
+
+      expect(find('.dropdown-menu')).to have_content 'empty-branch'
+    end
+  end
+
   def create_tag_in_form(tag:, ref:, message: nil, desc: nil)
     click_link 'New tag'
     fill_in 'tag_name', with: tag
-    fill_in 'ref', with: ref
+    find('#ref', visible: false).set(ref)
     fill_in 'message', with: message unless message.nil?
     fill_in 'release_description', with: desc unless desc.nil?
     click_button 'Create tag'
-- 
GitLab