Merge branch 'winh-dashboard-any-milestone' into 'master'

Reset milestone filter when clicking "Any Milestone" in dashboard

Closes #45295

See merge request gitlab-org/gitlab-ce!18531
This commit is contained in:
Phil Hughes 2018-04-26 19:08:48 +00:00
commit c73b5d31e6
3 changed files with 86 additions and 41 deletions

View File

@ -12,7 +12,8 @@ import ModalStore from './boards/stores/modal_store';
export default class MilestoneSelect { export default class MilestoneSelect {
constructor(currentProject, els, options = {}) { constructor(currentProject, els, options = {}) {
if (currentProject !== null) { if (currentProject !== null) {
this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; this.currentProject =
typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject;
} }
this.init(els, options); this.init(els, options);
@ -26,7 +27,10 @@ export default class MilestoneSelect {
} }
$els.each((i, dropdown) => { $els.each((i, dropdown) => {
let milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; let milestoneLinkNoneTemplate,
milestoneLinkTemplate,
selectedMilestone,
selectedMilestoneDefault;
const $dropdown = $(dropdown); const $dropdown = $(dropdown);
const projectId = $dropdown.data('projectId'); const projectId = $dropdown.data('projectId');
const milestonesUrl = $dropdown.data('milestones'); const milestonesUrl = $dropdown.data('milestones');
@ -46,45 +50,47 @@ export default class MilestoneSelect {
const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
const $value = $block.find('.value'); const $value = $block.find('.value');
const $loading = $block.find('.block-loading').fadeOut(); const $loading = $block.find('.block-loading').fadeOut();
selectedMilestoneDefault = (showAny ? '' : null); selectedMilestoneDefault = showAny ? '' : null;
selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); selectedMilestoneDefault = showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault;
selectedMilestone = $dropdown.data('selected') || 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>';
} }
return $dropdown.glDropdown({ return $dropdown.glDropdown({
showMenuAbove: showMenuAbove, showMenuAbove: showMenuAbove,
data: (term, callback) => axios.get(milestonesUrl) data: (term, callback) =>
.then(({ data }) => { axios.get(milestonesUrl).then(({ data }) => {
const extraOptions = []; const extraOptions = [];
if (showAny) { if (showAny) {
extraOptions.push({ extraOptions.push({
id: 0, id: null,
name: '', name: null,
title: 'Any Milestone' title: 'Any Milestone',
}); });
} }
if (showNo) { if (showNo) {
extraOptions.push({ extraOptions.push({
id: -1, id: -1,
name: 'No Milestone', name: 'No Milestone',
title: 'No Milestone' title: 'No Milestone',
}); });
} }
if (showUpcoming) { if (showUpcoming) {
extraOptions.push({ extraOptions.push({
id: -2, id: -2,
name: '#upcoming', name: '#upcoming',
title: 'Upcoming' title: 'Upcoming',
}); });
} }
if (showStarted) { if (showStarted) {
extraOptions.push({ extraOptions.push({
id: -3, id: -3,
name: '#started', name: '#started',
title: 'Started' title: 'Started',
}); });
} }
if (extraOptions.length) { if (extraOptions.length) {
@ -106,7 +112,7 @@ export default class MilestoneSelect {
`, `,
filterable: true, filterable: true,
search: { search: {
fields: ['title'] fields: ['title'],
}, },
selectable: true, selectable: true,
toggleLabel: (selected, el, e) => { toggleLabel: (selected, el, e) => {
@ -119,7 +125,7 @@ export default class MilestoneSelect {
defaultLabel: defaultLabel, defaultLabel: defaultLabel,
fieldName: $dropdown.data('fieldName'), fieldName: $dropdown.data('fieldName'),
text: milestone => _.escape(milestone.title), text: milestone => _.escape(milestone.title),
id: (milestone) => { id: milestone => {
if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) {
return milestone.name; return milestone.name;
} else { } else {
@ -131,7 +137,7 @@ export default class MilestoneSelect {
// display:block overrides the hide-collapse rule // display:block overrides the hide-collapse rule
return $value.css('display', ''); return $value.css('display', '');
}, },
opened: (e) => { opened: e => {
const $el = $(e.currentTarget); const $el = $(e.currentTarget);
if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) {
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
@ -140,7 +146,7 @@ export default class MilestoneSelect {
$(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`, $el).addClass('is-active'); $(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`, $el).addClass('is-active');
}, },
vue: $dropdown.hasClass('js-issue-board-sidebar'), vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: (clickEvent) => { clicked: clickEvent => {
const { $el, e } = clickEvent; const { $el, e } = clickEvent;
let selected = clickEvent.selectedObj; let selected = clickEvent.selectedObj;
@ -155,11 +161,14 @@ export default class MilestoneSelect {
const page = $('body').attr('data-page'); const page = $('body').attr('data-page');
const isIssueIndex = page === 'projects:issues:index'; const isIssueIndex = page === 'projects:issues:index';
const isMRIndex = (page === page && page === 'projects:merge_requests:index'); const isMRIndex = page === page && page === 'projects:merge_requests:index';
const isSelecting = (selected.name !== selectedMilestone); const isSelecting = selected.name !== selectedMilestone;
selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; 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;
} }
@ -177,10 +186,13 @@ export default class MilestoneSelect {
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 && isSelecting) { if (selected.id !== -1 && isSelecting) {
gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ gl.issueBoards.boardStoreIssueSet(
id: selected.id, 'milestone',
title: selected.name new ListMilestone({
})); id: selected.id,
title: selected.name,
}),
);
} else { } else {
gl.issueBoards.boardStoreIssueDelete('milestone'); gl.issueBoards.boardStoreIssueDelete('milestone');
} }
@ -188,7 +200,8 @@ export default class MilestoneSelect {
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
$loading.removeClass('hidden').fadeIn(); $loading.removeClass('hidden').fadeIn();
gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) gl.issueBoards.BoardsStore.detail.issue
.update($dropdown.attr('data-issue-update'))
.then(() => { .then(() => {
$dropdown.trigger('loaded.gl.dropdown'); $dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut(); $loading.fadeOut();
@ -203,7 +216,8 @@ export default class MilestoneSelect {
data[abilityName].milestone_id = selected != null ? selected : null; data[abilityName].milestone_id = selected != null ? selected : null;
$loading.removeClass('hidden').fadeIn(); $loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
return axios.put(issueUpdateURL, data) return axios
.put(issueUpdateURL, data)
.then(({ data }) => { .then(({ data }) => {
$dropdown.trigger('loaded.gl.dropdown'); $dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut(); $loading.fadeOut();
@ -215,7 +229,10 @@ export default class MilestoneSelect {
data.milestone.name = data.milestone.title; data.milestone.name = data.milestone.title;
$value.html(milestoneLinkTemplate(data.milestone)); $value.html(milestoneLinkTemplate(data.milestone));
return $sidebarCollapsedValue return $sidebarCollapsedValue
.attr('data-original-title', `${data.milestone.name}<br />${data.milestone.remaining}`) .attr(
'data-original-title',
`${data.milestone.name}<br />${data.milestone.remaining}`,
)
.find('span') .find('span')
.text(data.milestone.title); .text(data.milestone.title);
} else { } else {
@ -230,7 +247,7 @@ export default class MilestoneSelect {
$loading.fadeOut(); $loading.fadeOut();
}); });
} }
} },
}); });
}); });
} }

View File

@ -0,0 +1,5 @@
---
title: Reset milestone filter when clicking "Any Milestone" in dashboard
merge_request: 18531
author:
type: fixed

View File

@ -10,13 +10,16 @@ feature 'Dashboard > milestone filter', :js do
let!(:issue) { create :issue, author: user, project: project, milestone: milestone } let!(:issue) { create :issue, author: user, project: project, milestone: milestone }
let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 } let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
dropdown_toggle_button = '.js-milestone-select'
before do before do
sign_in(user) sign_in(user)
visit issues_dashboard_path(author_id: user.id)
end end
context 'default state' do context 'default state' do
it 'shows issues with Any Milestone' do it 'shows issues with Any Milestone' do
visit issues_dashboard_path(author_id: user.id)
page.all('.issue-info').each do |issue_info| page.all('.issue-info').each do |issue_info|
expect(issue_info.text).to match(/v\d.0/) expect(issue_info.text).to match(/v\d.0/)
end end
@ -24,31 +27,51 @@ feature 'Dashboard > milestone filter', :js do
end end
context 'filtering by milestone' do context 'filtering by milestone' do
milestone_select_selector = '.js-milestone-select'
before do before do
filter_item_select('v1.0', milestone_select_selector) visit issues_dashboard_path(author_id: user.id)
find(milestone_select_selector).click filter_item_select('v1.0', dropdown_toggle_button)
find(dropdown_toggle_button).click
wait_for_requests wait_for_requests
end end
it 'shows issues with Milestone v1.0' do it 'shows issues with Milestone v1.0' do
expect(find('.issues-list')).to have_selector('.issue', count: 1) expect(find('.issues-list')).to have_selector('.issue', count: 1)
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1) expect(find('.milestone-filter .dropdown-content')).to have_selector('a.is-active', count: 1)
end end
it 'should not change active Milestone unless clicked' do it 'should not change active Milestone unless clicked' do
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1) page.within '.milestone-filter' do
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
# open & close dropdown find('.dropdown-menu-close').click
find('.dropdown-menu-close').click
expect(find('.milestone-filter')).not_to have_selector('.dropdown.open') expect(page).not_to have_selector('.dropdown.open')
find(milestone_select_selector).click find(dropdown_toggle_button).click
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1) expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
expect(find('.dropdown-content a.is-active')).to have_content('v1.0') expect(find('.dropdown-content a.is-active')).to have_content('v1.0')
end
end
end
context 'with milestone filter in URL' do
before do
visit issues_dashboard_path(author_id: user.id, milestone_title: milestone.title)
find(dropdown_toggle_button).click
wait_for_requests
end
it 'has milestone selected' do
expect(find('.milestone-filter .dropdown-content')).to have_css('.is-active', text: milestone.title)
end
it 'removes milestone filter from URL after clicking "Any Milestone"' do
expect(current_url).to include("milestone_title=#{milestone.title}")
find('.milestone-filter .dropdown-content li', text: 'Any Milestone').click
expect(current_url).not_to include('milestone_title')
end end
end end
end end