From e5f104c137af02af7f19b8aa279cfb93ca3bf25b Mon Sep 17 00:00:00 2001 From: Jose Vargas Date: Mon, 5 Apr 2021 13:21:14 -0500 Subject: [PATCH 1/2] Move tags dropdown to gl-dropdown This the tags HAML dropdown to use the GlDropdown component from GitLab-ui --- .../pages/projects/tags/index/index.js | 2 + .../tags/components/sort_dropdown.vue | 77 ++++++++++++++++++ app/assets/javascripts/tags/index.js | 24 ++++++ app/controllers/projects/tags_controller.rb | 3 + app/helpers/tags_helper.rb | 4 + app/views/projects/tags/index.html.haml | 29 ++++--- .../development/gldropdown_tags.yml | 8 ++ .../tags/components/sort_dropdown_spec.js | 81 +++++++++++++++++++ spec/helpers/tags_helper_spec.rb | 20 +++++ .../projects/tags/index.html.haml_spec.rb | 1 + 10 files changed, 236 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/tags/components/sort_dropdown.vue create mode 100644 app/assets/javascripts/tags/index.js create mode 100644 config/feature_flags/development/gldropdown_tags.yml create mode 100644 spec/frontend/tags/components/sort_dropdown_spec.js create mode 100644 spec/helpers/tags_helper_spec.rb diff --git a/app/assets/javascripts/pages/projects/tags/index/index.js b/app/assets/javascripts/pages/projects/tags/index/index.js index 98560c1193b008..9e48dd9e46335d 100644 --- a/app/assets/javascripts/pages/projects/tags/index/index.js +++ b/app/assets/javascripts/pages/projects/tags/index/index.js @@ -1,3 +1,4 @@ +import TagSortDropdown from '~/tags'; import { initRemoveTag } from '../remove_tag'; initRemoveTag({ @@ -5,3 +6,4 @@ initRemoveTag({ document.querySelector(`[data-path="${path}"]`).closest('.js-tag-list').remove(); }, }); +TagSortDropdown(); diff --git a/app/assets/javascripts/tags/components/sort_dropdown.vue b/app/assets/javascripts/tags/components/sort_dropdown.vue new file mode 100644 index 00000000000000..08f25b88cff223 --- /dev/null +++ b/app/assets/javascripts/tags/components/sort_dropdown.vue @@ -0,0 +1,77 @@ + + diff --git a/app/assets/javascripts/tags/index.js b/app/assets/javascripts/tags/index.js new file mode 100644 index 00000000000000..68510f3fe3a710 --- /dev/null +++ b/app/assets/javascripts/tags/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import SortDropdown from './components/sort_dropdown.vue'; + +const mountDropdownApp = (el) => { + const { sortOptions, filterTagsPath } = el.dataset; + + return new Vue({ + el, + name: 'SortTagsDropdownApp', + components: { + SortDropdown, + }, + provide: { + sortOptions: JSON.parse(sortOptions), + filterTagsPath, + }, + render: (createElement) => createElement(SortDropdown), + }); +}; + +export default () => { + const el = document.getElementById('js-tags-sort-dropdown'); + return el ? mountDropdownApp(el) : null; +}; diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 94b0473e1f3876..6ef4fc7c21d21d 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -9,6 +9,9 @@ class Projects::TagsController < Projects::ApplicationController before_action :require_non_empty_project before_action :authorize_download_code! before_action :authorize_admin_tag!, only: [:new, :create, :destroy] + before_action do + push_frontend_feature_flag(:gldropdown_tags) + end feature_category :source_code_management, [:index, :show, :new, :destroy] feature_category :release_evidence, [:create] diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index bfc8803f514763..09f9b2b3004955 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -47,4 +47,8 @@ def delete_tag_modal_attributes(tag_name) okTitle: s_('TagsPage|Delete tag') }.to_json end + + def gldropdown_tags_enabled? + Feature.enabled?(:gldropdown_tags) + end end diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index b5b394723ab09c..855d1804a016cc 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -9,20 +9,23 @@ = s_('TagsPage|Tags give the ability to mark specific points in history as being important') .nav-controls - = form_tag(filter_tags_path, method: :get) do - = search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } + - if !gldropdown_tags_enabled? + = form_tag(filter_tags_path, method: :get) do + = search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } - .dropdown - %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} } - %span.light - = tags_sort_options_hash[@sort] - = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3') - %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable - %li.dropdown-header - = s_('TagsPage|Sort by') - - tags_sort_options_hash.each do |value, title| - %li - = link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value) + .dropdown + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} } + %span.light + = tags_sort_options_hash[@sort] + = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3') + %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable + %li.dropdown-header + = s_('TagsPage|Sort by') + - tags_sort_options_hash.each do |value, title| + %li + = link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value) + - else + #js-tags-sort-dropdown{ data: { filter_tags_path: filter_tags_path, sort_options: tags_sort_options_hash.to_json } } - if can?(current_user, :admin_tag, @project) = link_to new_project_tag_path(@project), class: 'btn gl-button btn-confirm', data: { qa_selector: "new_tag_button" } do = s_('TagsPage|New tag') diff --git a/config/feature_flags/development/gldropdown_tags.yml b/config/feature_flags/development/gldropdown_tags.yml new file mode 100644 index 00000000000000..704f276ac372da --- /dev/null +++ b/config/feature_flags/development/gldropdown_tags.yml @@ -0,0 +1,8 @@ +--- +name: gldropdown_tags +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58589 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327055 +milestone: '13.11' +type: development +group: group::continuous integration +default_enabled: false diff --git a/spec/frontend/tags/components/sort_dropdown_spec.js b/spec/frontend/tags/components/sort_dropdown_spec.js new file mode 100644 index 00000000000000..b0fd98ec68e18e --- /dev/null +++ b/spec/frontend/tags/components/sort_dropdown_spec.js @@ -0,0 +1,81 @@ +import { GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import * as urlUtils from '~/lib/utils/url_utility'; +import SortDropdown from '~/tags/components/sort_dropdown.vue'; + +describe('Tags sort dropdown', () => { + let wrapper; + + const createWrapper = (props = {}) => { + return extendedWrapper( + mount(SortDropdown, { + provide: { + filterTagsPath: '/root/ci-cd-project-demo/-/tags', + sortOptions: { + name_asc: 'Name', + updated_asc: 'Oldest updated', + updated_desc: 'Last updated', + }, + ...props, + }, + }), + ); + }; + + const findSearchBox = () => wrapper.findComponent(GlSearchBoxByClick); + const findTagsDropdown = () => wrapper.findByTestId('tags-dropdown'); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + } + }); + + describe('default state', () => { + beforeEach(() => { + wrapper = createWrapper(); + }); + + it('should have a search box with a placeholder', () => { + const searchBox = findSearchBox(); + + expect(searchBox.exists()).toBe(true); + expect(searchBox.find('input').attributes('placeholder')).toBe('Filter by tag name'); + }); + + it('should have a sort order dropdown', () => { + const branchesDropdown = findTagsDropdown(); + + expect(branchesDropdown.exists()).toBe(true); + }); + }); + + describe('when submitting a search term', () => { + beforeEach(() => { + urlUtils.visitUrl = jest.fn(); + + wrapper = createWrapper(); + }); + + it('should call visitUrl', () => { + const searchBox = findSearchBox(); + + searchBox.vm.$emit('submit'); + + expect(urlUtils.visitUrl).toHaveBeenCalledWith( + '/root/ci-cd-project-demo/-/tags?sort=updated_desc', + ); + }); + + it('should send a sort parameter', () => { + const sortDropdownItems = findTagsDropdown().findAllComponents(GlDropdownItem).at(0); + + sortDropdownItems.vm.$emit('click'); + + expect(urlUtils.visitUrl).toHaveBeenCalledWith( + '/root/ci-cd-project-demo/-/tags?sort=name_asc', + ); + }); + }); +}); diff --git a/spec/helpers/tags_helper_spec.rb b/spec/helpers/tags_helper_spec.rb new file mode 100644 index 00000000000000..c680b64a61bc54 --- /dev/null +++ b/spec/helpers/tags_helper_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe TagsHelper do + describe '#gl_dropdown_tags_enabled?' do + context 'when the feature is enabled' do + it 'returns true' do + expect(helper.gldropdown_tags_enabled?).to be_truthy + end + end + + context 'when the feature is disabled' do + it 'returns false' do + stub_feature_flags(gldropdown_tags: false) + expect(helper.gldropdown_tags_enabled?).to be_falsy + end + end + end +end diff --git a/spec/views/projects/tags/index.html.haml_spec.rb b/spec/views/projects/tags/index.html.haml_spec.rb index dc008875062f6c..18b42f98e0b47e 100644 --- a/spec/views/projects/tags/index.html.haml_spec.rb +++ b/spec/views/projects/tags/index.html.haml_spec.rb @@ -21,6 +21,7 @@ end it 'defaults sort dropdown toggle to last updated' do + stub_feature_flags(gldropdown_tags: false) render expect(rendered).to have_button('Last updated') end -- GitLab From bd481e293d6197ea323e996153067db1bbe5e06f Mon Sep 17 00:00:00 2001 From: Jose Vargas Date: Fri, 9 Apr 2021 12:25:32 -0500 Subject: [PATCH 2/2] Move feature flag to features helper This also addresses some smaller code concerns --- .../tags/components/sort_dropdown.vue | 6 +++--- app/controllers/projects/tags_controller.rb | 2 +- app/helpers/tags_helper.rb | 4 ---- app/views/projects/tags/index.html.haml | 2 +- lib/gitlab/ci/features.rb | 4 ++++ spec/helpers/tags_helper_spec.rb | 20 ------------------- 6 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 spec/helpers/tags_helper_spec.rb diff --git a/app/assets/javascripts/tags/components/sort_dropdown.vue b/app/assets/javascripts/tags/components/sort_dropdown.vue index 08f25b88cff223..036ce2cca78968 100644 --- a/app/assets/javascripts/tags/components/sort_dropdown.vue +++ b/app/assets/javascripts/tags/components/sort_dropdown.vue @@ -54,15 +54,15 @@ export default { };