Commit e5f104c1 authored by Jose Vargas's avatar Jose Vargas

Move tags dropdown to gl-dropdown

This the tags HAML dropdown to use
the GlDropdown component from GitLab-ui
parent fc4ba137
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();
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { mergeUrlParams, visitUrl, getParameterValues } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default {
i18n: {
searchPlaceholder: s__('TagsPage|Filter by tag name'),
},
components: {
GlDropdown,
GlDropdownItem,
GlSearchBoxByClick,
},
inject: ['sortOptions', 'filterTagsPath'],
data() {
return {
selectedKey: 'updated_desc',
searchTerm: '',
};
},
computed: {
selectedSortMethod() {
return this.sortOptions[this.selectedKey];
},
},
created() {
const sortValue = getParameterValues('sort');
const searchValue = getParameterValues('search');
if (sortValue.length > 0) {
[this.selectedKey] = sortValue;
}
if (searchValue.length > 0) {
[this.searchTerm] = searchValue;
}
},
methods: {
isSortMethodSelected(sortKey) {
return sortKey === this.selectedKey;
},
visitUrlFromOption(sortKey) {
this.selectedKey = sortKey;
const urlParams = {};
urlParams.search = this.searchTerm.length > 0 ? this.searchTerm : null;
urlParams.sort = sortKey;
const newUrl = mergeUrlParams(urlParams, this.filterTagsPath);
visitUrl(newUrl);
},
},
};
</script>
<template>
<div class="gl-display-flex gl-pr-4">
<gl-search-box-by-click
v-model="searchTerm"
:placeholder="$options.i18n.searchPlaceholder"
class="gl-pr-4"
data-testid="tag-search"
@submit="visitUrlFromOption(selectedKey)"
/>
<gl-dropdown :text="selectedSortMethod" data-testid="tags-dropdown">
<gl-dropdown-item
v-for="(value, key) in sortOptions"
:key="key"
:is-checked="isSortMethodSelected(key)"
is-check-item
@click="visitUrlFromOption(key)"
>
{{ value }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>
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;
};
......@@ -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]
......
......@@ -47,4 +47,8 @@ module TagsHelper
okTitle: s_('TagsPage|Delete tag')
}.to_json
end
def gldropdown_tags_enabled?
Feature.enabled?(:gldropdown_tags)
end
end
......@@ -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')
......
---
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
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',
);
});
});
});
# 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
......@@ -21,6 +21,7 @@ RSpec.describe 'projects/tags/index.html.haml' do
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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment