Disallow multiselect for Milestone dropdown
This commit is contained in:
parent
f59a44dbdf
commit
f328f38e5b
7 changed files with 97 additions and 13 deletions
|
@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
|||
},
|
||||
assigneeId() {
|
||||
return this.issue.assignee ? this.issue.assignee.id : 0;
|
||||
},
|
||||
milestoneTitle() {
|
||||
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -18,12 +18,11 @@
|
|||
}
|
||||
|
||||
$els.each(function(i, dropdown) {
|
||||
var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove;
|
||||
var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove;
|
||||
$dropdown = $(dropdown);
|
||||
projectId = $dropdown.data('project-id');
|
||||
milestonesUrl = $dropdown.data('milestones');
|
||||
issueUpdateURL = $dropdown.data('issueUpdate');
|
||||
selectedMilestone = $dropdown.data('selected');
|
||||
showNo = $dropdown.data('show-no');
|
||||
showAny = $dropdown.data('show-any');
|
||||
showMenuAbove = $dropdown.data('showMenuAbove');
|
||||
|
@ -31,6 +30,7 @@
|
|||
showStarted = $dropdown.data('show-started');
|
||||
useId = $dropdown.data('use-id');
|
||||
defaultLabel = $dropdown.data('default-label');
|
||||
defaultNo = $dropdown.data('default-no');
|
||||
issuableId = $dropdown.data('issuable-id');
|
||||
abilityName = $dropdown.data('ability-name');
|
||||
$selectbox = $dropdown.closest('.selectbox');
|
||||
|
@ -38,6 +38,9 @@
|
|||
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
|
||||
$value = $block.find('.value');
|
||||
$loading = $block.find('.block-loading').fadeOut();
|
||||
selectedMilestoneDefault = (showAny ? '' : null);
|
||||
selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault);
|
||||
selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault;
|
||||
if (issueUpdateURL) {
|
||||
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
|
||||
milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
|
||||
|
@ -86,8 +89,18 @@
|
|||
if (showMenuAbove) {
|
||||
$dropdown.data('glDropdown').positionMenuAbove();
|
||||
}
|
||||
$(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active');
|
||||
});
|
||||
},
|
||||
renderRow: function(milestone) {
|
||||
return `
|
||||
<li data-milestone-id="${milestone.name}">
|
||||
<a href='#' class='dropdown-menu-milestone-link'>
|
||||
${_.escape(milestone.title)}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
},
|
||||
filterable: true,
|
||||
search: {
|
||||
fields: ['title']
|
||||
|
@ -120,15 +133,24 @@
|
|||
// display:block overrides the hide-collapse rule
|
||||
return $value.css('display', '');
|
||||
},
|
||||
opened: function(e) {
|
||||
const $el = $(e.currentTarget);
|
||||
if ($dropdown.hasClass('js-issue-board-sidebar')) {
|
||||
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
|
||||
}
|
||||
$('a.is-active', $el).removeClass('is-active');
|
||||
$(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active');
|
||||
},
|
||||
vue: $dropdown.hasClass('js-issue-board-sidebar'),
|
||||
clicked: function(options) {
|
||||
const { $el, e } = options;
|
||||
let selected = options.selectedObj;
|
||||
|
||||
var data, isIssueIndex, isMRIndex, page, boardsStore;
|
||||
var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore;
|
||||
page = $('body').data('page');
|
||||
isIssueIndex = page === 'projects:issues:index';
|
||||
isMRIndex = (page === page && page === 'projects:merge_requests:index');
|
||||
isSelecting = (selected.name !== selectedMilestone);
|
||||
selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault;
|
||||
if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
@ -142,16 +164,11 @@
|
|||
boardsStore[$dropdown.data('field-name')] = selected.name;
|
||||
e.preventDefault();
|
||||
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
|
||||
if (selected.name != null) {
|
||||
selectedMilestone = selected.name;
|
||||
} else {
|
||||
selectedMilestone = '';
|
||||
}
|
||||
return Issuable.filterResults($dropdown.closest('form'));
|
||||
} else if ($dropdown.hasClass('js-filter-submit')) {
|
||||
return $dropdown.closest('form').submit();
|
||||
} else if ($dropdown.hasClass('js-issue-board-sidebar')) {
|
||||
if (selected.id !== -1) {
|
||||
if (selected.id !== -1 && isSelecting) {
|
||||
gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({
|
||||
id: selected.id,
|
||||
title: selected.name
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
name: "issue[milestone_id]",
|
||||
"v-if" => "issue.milestone" }
|
||||
.dropdown
|
||||
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" },
|
||||
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true", default_no: "true" },
|
||||
":data-selected" => "milestoneTitle",
|
||||
":data-issuable-id" => "issue.id",
|
||||
":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
|
||||
Milestone
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
- if selected.present? || params[:milestone_title].present?
|
||||
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
|
||||
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
|
||||
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
|
||||
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
|
||||
- if project
|
||||
%ul.dropdown-footer-list
|
||||
- if can? current_user, :admin_milestone, project
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
|
||||
.selectbox.hide-collapsed
|
||||
= f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil
|
||||
= dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }})
|
||||
= dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true }})
|
||||
- if issuable.has_attribute?(:time_estimate)
|
||||
#issuable-time-tracker.block
|
||||
// Fallback while content is loading
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Disallow multiple selections for Milestone dropdown
|
||||
merge_request: 11084
|
||||
author:
|
59
spec/features/dashboard/milestone_filter_spec.rb
Normal file
59
spec/features/dashboard/milestone_filter_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Dashboard > milestone filter', feature: true, js: true do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
|
||||
let(:milestone) { create(:milestone, title: "v1.0", project: project) }
|
||||
let(:milestone2) { create(:milestone, title: "v2.0", project: project) }
|
||||
let!(:issue) { create :issue, author: user, project: project, milestone: milestone }
|
||||
let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
visit issues_dashboard_path(author_id: user.id)
|
||||
end
|
||||
|
||||
context 'default state' do
|
||||
it 'shows issues with Any Milestone' do
|
||||
page.all('.issue-info').each do |issue_info|
|
||||
expect(issue_info.text).to match(/v\d.0/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'filtering by milestone' do
|
||||
milestone_select = '.js-milestone-select'
|
||||
|
||||
before do
|
||||
find(milestone_select).click
|
||||
|
||||
page.within('.dropdown-content') do
|
||||
click_link 'v1.0'
|
||||
end
|
||||
|
||||
find(milestone_select).click
|
||||
end
|
||||
|
||||
it 'shows issues with Milestone v1.0' do
|
||||
expect(find('.issues-list')).to have_selector('.issue', count: 1)
|
||||
|
||||
find(milestone_select).click
|
||||
|
||||
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
|
||||
end
|
||||
|
||||
it 'should not change active Milestone unless clicked' do
|
||||
find(milestone_select).click
|
||||
|
||||
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
|
||||
|
||||
# open & close dropdown
|
||||
find('.dropdown-menu-close').click
|
||||
expect(find('.milestone-filter')).not_to have_selector('.dropdown.open')
|
||||
find(milestone_select).click
|
||||
|
||||
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
|
||||
expect(find('.dropdown-content a.is-active')).to have_content('v1.0')
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue