Commit 57d32dd9 authored by Tom Quirk's avatar Tom Quirk

Use GlDropdown for Issue Status select

This commit adds a Vue app for
the issue status dropdown on the
issuable bulk update sidebar.

Bulk update sidebar-related files
are moved into a single directory,
and imports have been updated.

No user-facing changes are present
in this MR
parent 2aa4fa13
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
name: 'StatusSelect',
components: {
GlDropdown,
GlDropdownItem,
},
data() {
return {
status: null,
};
},
computed: {
dropdownText() {
return this.status?.text ?? __('Select status');
},
selectedValue() {
return this.status?.value;
},
},
selectOptions: [
{
value: 'reopen',
text: __('Open'),
},
{
value: 'close',
text: __('Closed'),
},
],
};
</script>
<template>
<div>
<input type="hidden" name="update[state_event]" :value="selectedValue" />
<gl-dropdown :text="dropdownText" :title="__('Change status')" class="gl-w-full">
<gl-dropdown-item
v-for="item in $options.selectOptions"
:key="item.value"
:is-checked="selectedValue === item.value"
is-check-item
:title="item.text"
@click="status = item"
>
{{ item.text }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>
import Vue from 'vue';
import StatusSelect from './components/status_select.vue';
export default function initIssueStatusSelect() {
const el = document.querySelector('.js-issue-status');
if (!el) {
return null;
}
return new Vue({
el,
render(h) {
return h(StatusSelect);
},
});
}
import $ from 'jquery'; import $ from 'jquery';
import { difference, intersection, union } from 'lodash'; import { difference, intersection, union } from 'lodash';
import createFlash from './flash'; import createFlash from '~/flash';
import axios from './lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from './locale'; import { __ } from '~/locale';
export default { export default {
init({ form, issues, prefixId } = {}) { init({ form, issues, prefixId } = {}) {
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
import $ from 'jquery'; import $ from 'jquery';
import { property } from 'lodash'; import { property } from 'lodash';
import issueableEventHub from '~/issues_list/eventhub';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
import initIssueStatusSelect from './init_issue_status_select';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import issueStatusSelect from './issue_status_select';
import issueableEventHub from './issues_list/eventhub';
import LabelsSelect from './labels_select';
import MilestoneSelect from './milestone_select';
import subscriptionSelect from './subscription_select'; import subscriptionSelect from './subscription_select';
const HIDDEN_CLASS = 'hidden'; const HIDDEN_CLASS = 'hidden';
...@@ -29,7 +30,7 @@ export default class IssuableBulkUpdateSidebar { ...@@ -29,7 +30,7 @@ export default class IssuableBulkUpdateSidebar {
this.$sidebar = $('.right-sidebar'); this.$sidebar = $('.right-sidebar');
this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar'); this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide'); this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
this.$bulkEditSubmitBtn = $('.update-selected-issues'); this.$bulkEditSubmitBtn = $('.js-update-selected-issues');
this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle'); this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle');
this.$otherFilters = $('.issues-other-filters'); this.$otherFilters = $('.issues-other-filters');
this.$checkAllContainer = $('.check-all-holder'); this.$checkAllContainer = $('.check-all-holder');
...@@ -56,7 +57,7 @@ export default class IssuableBulkUpdateSidebar { ...@@ -56,7 +57,7 @@ export default class IssuableBulkUpdateSidebar {
initDropdowns() { initDropdowns() {
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
issueStatusSelect(); initIssueStatusSelect();
subscriptionSelect(); subscriptionSelect();
if (IS_EE) { if (IS_EE) {
......
import $ from 'jquery'; import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { __ } from './locale'; import { __ } from '~/locale';
export default function subscriptionSelect() { export default function subscriptionSelect() {
$('.js-subscription-event').each((i, element) => { $('.js-subscription-event').each((i, element) => {
......
import issuableInitBulkUpdateSidebar from './issuable_init_bulk_update_sidebar'; import issuableInitBulkUpdateSidebar from '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar';
export default class IssuableIndex { export default class IssuableIndex {
constructor(pagePrefix = 'issuable_') { constructor(pagePrefix = 'issuable_') {
......
import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { __ } from './locale';
export default function issueStatusSelect() {
$('.js-issue-status').each((i, el) => {
const fieldName = $(el).data('fieldName');
initDeprecatedJQueryDropdown($(el), {
selectable: true,
fieldName,
toggleLabel(selected, element, instance) {
let label = __('Author');
const $item = instance.dropdown.find('.is-active');
if ($item.length) {
label = $item.text();
}
return label;
},
clicked(options) {
return options.e.preventDefault();
},
id(obj, element) {
return $(element).data('id');
},
});
});
}
...@@ -450,7 +450,9 @@ export default { ...@@ -450,7 +450,9 @@ export default {
}, },
async handleBulkUpdateClick() { async handleBulkUpdateClick() {
if (!this.hasInitBulkEdit) { if (!this.hasInitBulkEdit) {
const initBulkUpdateSidebar = await import('~/issuable_init_bulk_update_sidebar'); const initBulkUpdateSidebar = await import(
'~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar'
);
initBulkUpdateSidebar.default.init('issuable_'); initBulkUpdateSidebar.default.init('issuable_');
const usersSelect = await import('~/users_select'); const usersSelect = await import('~/users_select');
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
import $ from 'jquery'; import $ from 'jquery';
import { difference, isEqual, escape, sortBy, template, union } from 'lodash'; import { difference, isEqual, escape, sortBy, template, union } from 'lodash';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import IssuableBulkUpdateActions from '~/issuable_bulk_update_sidebar/issuable_bulk_update_actions';
import { isScopedLabel } from '~/lib/utils/common_utils'; import { isScopedLabel } from '~/lib/utils/common_utils';
import boardsStore from './boards/stores/boards_store'; import boardsStore from './boards/stores/boards_store';
import CreateLabelDropdown from './create_label'; import CreateLabelDropdown from './create_label';
import createFlash from './flash'; import createFlash from './flash';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { sprintf, __ } from './locale'; import { sprintf, __ } from './locale';
......
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; import issuableInitBulkUpdateSidebar from '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar';
import { mountIssuablesListApp } from '~/issues_list'; import { mountIssuablesListApp } from '~/issues_list';
import initManualOrdering from '~/manual_ordering'; import initManualOrdering from '~/manual_ordering';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
......
import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; import issuableInitBulkUpdateSidebar from '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
......
...@@ -6,21 +6,13 @@ ...@@ -6,21 +6,13 @@
= form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do = form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.float-left .filter-item.inline.update-issues-btn.float-left
= button_tag _('Update all'), class: "gl-button btn update-selected-issues btn-confirm", disabled: true = button_tag _('Update all'), class: "gl-button btn js-update-selected-issues btn-confirm", disabled: true
= button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right" = button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right"
- if params[:state] != 'merged' - if params[:state] != 'merged'
.block .block
.title .title
= _('Status') = _('Status')
.filter-item .js-issue-status
= dropdown_tag(_("Select status"), options: { toggle_class: "js-issue-status", title: _("Change status"), dropdown_class: "dropdown-menu-status dropdown-menu-selectable", data: { field_name: "update[state_event]", default_label: _("Status") } } ) do
%ul
%li
%a{ href: "#", data: { id: "reopen" } }
= _('Open')
%li
%a{ href: "#", data: { id: "close" } }
= _('Closed')
.block .block
.title .title
= _('Assignee') = _('Assignee')
......
import initEpicCreateApp from 'ee/epic/epic_bundle'; import initEpicCreateApp from 'ee/epic/epic_bundle';
import initEpicsList from 'ee/epics_list/epics_list_bundle'; import initEpicsList from 'ee/epics_list/epics_list_bundle';
import FilteredSearchTokenKeysEpics from 'ee/filtered_search/filtered_search_token_keys_epics'; import FilteredSearchTokenKeysEpics from 'ee/filtered_search/filtered_search_token_keys_epics';
import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; import issuableInitBulkUpdateSidebar from '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
const EPIC_BULK_UPDATE_PREFIX = 'epic_'; const EPIC_BULK_UPDATE_PREFIX = 'epic_';
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= form_tag [:bulk_update, group, type], method: :post, class: "bulk-update" do = form_tag [:bulk_update, group, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.float-left .filter-item.inline.update-issues-btn.float-left
= button_tag _('Update all'), class: "gl-button btn update-selected-issues btn-confirm", disabled: true = button_tag _('Update all'), class: "gl-button btn js-update-selected-issues btn-confirm", disabled: true
= button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right" = button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right"
- if is_issue - if is_issue
= render "shared/issuable/epic_dropdown", parent: group = render "shared/issuable/epic_dropdown", parent: group
......
...@@ -13,26 +13,26 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do ...@@ -13,26 +13,26 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
end end
context 'status' do context 'status' do
it 'sets to closed' do it 'sets to closed', :js do
visit project_issues_path(project) visit project_issues_path(project)
click_button 'Edit issues' click_button 'Edit issues'
check 'Select all' check 'Select all'
click_button 'Select status' click_button 'Select status'
click_link 'Closed' click_button 'Closed'
click_update_issues_button click_update_issues_button
expect(page).to have_selector('.issue', count: 0) expect(page).to have_selector('.issue', count: 0)
end end
it 'sets to open' do it 'sets to open', :js do
create_closed create_closed
visit project_issues_path(project, state: 'closed') visit project_issues_path(project, state: 'closed')
click_button 'Edit issues' click_button 'Edit issues'
check 'Select all' check 'Select all'
click_button 'Select status' click_button 'Select status'
click_link 'Open' click_button 'Open'
click_update_issues_button click_update_issues_button
expect(page).to have_selector('.issue', count: 0) expect(page).to have_selector('.issue', count: 0)
......
...@@ -18,7 +18,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do ...@@ -18,7 +18,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
visit project_merge_requests_path(project) visit project_merge_requests_path(project)
end end
it 'closes merge request' do it 'closes merge request', :js do
change_status('Closed') change_status('Closed')
expect(page).to have_selector('.merge-request', count: 0) expect(page).to have_selector('.merge-request', count: 0)
...@@ -31,7 +31,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do ...@@ -31,7 +31,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
visit project_merge_requests_path(project, state: 'closed') visit project_merge_requests_path(project, state: 'closed')
end end
it 'reopens merge request' do it 'reopens merge request', :js do
change_status('Open') change_status('Open')
expect(page).to have_selector('.merge-request', count: 0) expect(page).to have_selector('.merge-request', count: 0)
...@@ -109,7 +109,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do ...@@ -109,7 +109,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
click_button 'Edit merge requests' click_button 'Edit merge requests'
check 'Select all' check 'Select all'
click_button 'Select status' click_button 'Select status'
click_link text click_button text
click_update_merge_requests_button click_update_merge_requests_button
end end
......
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import StatusSelect from '~/issuable_bulk_update_sidebar/components/status_select.vue';
describe('StatusSelect', () => {
let wrapper;
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findHiddenInput = () => wrapper.find('input');
function createComponent() {
wrapper = shallowMount(StatusSelect);
}
describe('with no value selected', () => {
it('renders default text', () => {
createComponent();
expect(findDropdown().props('text')).toBe('Select status');
});
it('renders dropdown items with `is-checked` prop set to `false`', () => {
const dropdownItems = findAllDropdownItems();
expect(dropdownItems.at(0).props('isChecked')).toBe(false);
expect(dropdownItems.at(1).props('isChecked')).toBe(false);
});
});
describe('when selecting a value', () => {
beforeEach(async () => {
createComponent();
findAllDropdownItems().at(0).vm.$emit('click');
await wrapper.vm.$nextTick();
});
it('updates value of the hidden input', () => {
expect(findHiddenInput().attributes('value')).toBe('reopen');
});
it('updates the dropdown text prop', () => {
expect(findDropdown().props('text')).toBe('Open');
});
it('sets dropdown item `is-checked` prop to `true`', () => {
const dropdownItems = findAllDropdownItems();
expect(dropdownItems.at(0).props('isChecked')).toBe(true);
expect(dropdownItems.at(1).props('isChecked')).toBe(false);
});
});
});
import issuableInitBulkUpdateSidebar from '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar';
import IssuableIndex from '~/issuable_index'; import IssuableIndex from '~/issuable_index';
import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar';
describe('Issuable', () => { describe('Issuable', () => {
describe('initBulkUpdate', () => { describe('initBulkUpdate', () => {
......
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