Commit f328f38e authored by Sam Rose's avatar Sam Rose

Disallow multiselect for Milestone dropdown

parent f59a44db
...@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({ ...@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({
}, },
assigneeId() { assigneeId() {
return this.issue.assignee ? this.issue.assignee.id : 0; return this.issue.assignee ? this.issue.assignee.id : 0;
},
milestoneTitle() {
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
} }
}, },
watch: { watch: {
......
...@@ -18,12 +18,11 @@ ...@@ -18,12 +18,11 @@
} }
$els.each(function(i, dropdown) { $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); $dropdown = $(dropdown);
projectId = $dropdown.data('project-id'); projectId = $dropdown.data('project-id');
milestonesUrl = $dropdown.data('milestones'); milestonesUrl = $dropdown.data('milestones');
issueUpdateURL = $dropdown.data('issueUpdate'); issueUpdateURL = $dropdown.data('issueUpdate');
selectedMilestone = $dropdown.data('selected');
showNo = $dropdown.data('show-no'); showNo = $dropdown.data('show-no');
showAny = $dropdown.data('show-any'); showAny = $dropdown.data('show-any');
showMenuAbove = $dropdown.data('showMenuAbove'); showMenuAbove = $dropdown.data('showMenuAbove');
...@@ -31,6 +30,7 @@ ...@@ -31,6 +30,7 @@
showStarted = $dropdown.data('show-started'); showStarted = $dropdown.data('show-started');
useId = $dropdown.data('use-id'); useId = $dropdown.data('use-id');
defaultLabel = $dropdown.data('default-label'); defaultLabel = $dropdown.data('default-label');
defaultNo = $dropdown.data('default-no');
issuableId = $dropdown.data('issuable-id'); issuableId = $dropdown.data('issuable-id');
abilityName = $dropdown.data('ability-name'); abilityName = $dropdown.data('ability-name');
$selectbox = $dropdown.closest('.selectbox'); $selectbox = $dropdown.closest('.selectbox');
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
$value = $block.find('.value'); $value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
selectedMilestoneDefault = (showAny ? '' : null);
selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault);
selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault;
if (issueUpdateURL) { if (issueUpdateURL) {
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); 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>'; milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
...@@ -86,8 +89,18 @@ ...@@ -86,8 +89,18 @@
if (showMenuAbove) { if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove(); $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, filterable: true,
search: { search: {
fields: ['title'] fields: ['title']
...@@ -120,15 +133,24 @@ ...@@ -120,15 +133,24 @@
// display:block overrides the hide-collapse rule // display:block overrides the hide-collapse rule
return $value.css('display', ''); 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'), vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(options) { clicked: function(options) {
const { $el, e } = options; const { $el, e } = options;
let selected = options.selectedObj; let selected = options.selectedObj;
var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore;
var data, isIssueIndex, isMRIndex, page, boardsStore;
page = $('body').data('page'); page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index'; isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests: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')) { if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
e.preventDefault(); e.preventDefault();
return; return;
...@@ -142,16 +164,11 @@ ...@@ -142,16 +164,11 @@
boardsStore[$dropdown.data('field-name')] = selected.name; boardsStore[$dropdown.data('field-name')] = selected.name;
e.preventDefault(); e.preventDefault();
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
if (selected.name != null) {
selectedMilestone = selected.name;
} else {
selectedMilestone = '';
}
return Issuable.filterResults($dropdown.closest('form')); return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) { } else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit(); return $dropdown.closest('form').submit();
} else if ($dropdown.hasClass('js-issue-board-sidebar')) { } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if (selected.id !== -1) { if (selected.id !== -1 && isSelecting) {
gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({
id: selected.id, id: selected.id,
title: selected.name title: selected.name
......
...@@ -16,7 +16,8 @@ ...@@ -16,7 +16,8 @@
name: "issue[milestone_id]", name: "issue[milestone_id]",
"v-if" => "issue.milestone" } "v-if" => "issue.milestone" }
.dropdown .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-issuable-id" => "issue.id",
":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
Milestone Milestone
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- if selected.present? || params[:milestone_title].present? - if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id) = 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", = 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 - if project
%ul.dropdown-footer-list %ul.dropdown-footer-list
- if can? current_user, :admin_milestone, project - if can? current_user, :admin_milestone, project
......
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
.selectbox.hide-collapsed .selectbox.hide-collapsed
= f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil = 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) - if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block #issuable-time-tracker.block
// Fallback while content is loading // Fallback while content is loading
......
---
title: Disallow multiple selections for Milestone dropdown
merge_request: 11084
author:
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
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