diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 7e9a50a885d34b873da69702d83a40c946a7f11b..f8b3d3061f05fcedfa0d260e3e2a10569b3acbc0 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -12,7 +12,8 @@ import ModalStore from './boards/stores/modal_store'; export default class MilestoneSelect { constructor(currentProject, els, options = {}) { 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); @@ -26,7 +27,10 @@ export default class MilestoneSelect { } $els.each((i, dropdown) => { - let milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; + let milestoneLinkNoneTemplate, + milestoneLinkTemplate, + selectedMilestone, + selectedMilestoneDefault; const $dropdown = $(dropdown); const projectId = $dropdown.data('projectId'); const milestonesUrl = $dropdown.data('milestones'); @@ -46,45 +50,47 @@ export default class MilestoneSelect { const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); const $value = $block.find('.value'); const $loading = $block.find('.block-loading').fadeOut(); - selectedMilestoneDefault = (showAny ? '' : null); - selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); + selectedMilestoneDefault = showAny ? '' : null; + selectedMilestoneDefault = showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault; selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; if (issueUpdateURL) { - milestoneLinkTemplate = _.template('<%- title %>'); + milestoneLinkTemplate = _.template( + '<%- title %>', + ); milestoneLinkNoneTemplate = 'None'; } return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, - data: (term, callback) => axios.get(milestonesUrl) - .then(({ data }) => { + data: (term, callback) => + axios.get(milestonesUrl).then(({ data }) => { const extraOptions = []; if (showAny) { extraOptions.push({ - id: 0, - name: '', - title: 'Any Milestone' + id: null, + name: null, + title: 'Any Milestone', }); } if (showNo) { extraOptions.push({ id: -1, name: 'No Milestone', - title: 'No Milestone' + title: 'No Milestone', }); } if (showUpcoming) { extraOptions.push({ id: -2, name: '#upcoming', - title: 'Upcoming' + title: 'Upcoming', }); } if (showStarted) { extraOptions.push({ id: -3, name: '#started', - title: 'Started' + title: 'Started', }); } if (extraOptions.length) { @@ -106,7 +112,7 @@ export default class MilestoneSelect { `, filterable: true, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, toggleLabel: (selected, el, e) => { @@ -119,7 +125,7 @@ export default class MilestoneSelect { defaultLabel: defaultLabel, fieldName: $dropdown.data('fieldName'), text: milestone => _.escape(milestone.title), - id: (milestone) => { + id: milestone => { if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { return milestone.name; } else { @@ -131,7 +137,7 @@ export default class MilestoneSelect { // display:block overrides the hide-collapse rule return $value.css('display', ''); }, - opened: (e) => { + opened: e => { const $el = $(e.currentTarget); if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; @@ -140,7 +146,7 @@ export default class MilestoneSelect { $(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`, $el).addClass('is-active'); }, vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: (clickEvent) => { + clicked: clickEvent => { const { $el, e } = clickEvent; let selected = clickEvent.selectedObj; @@ -155,11 +161,14 @@ export default class MilestoneSelect { const page = $('body').attr('data-page'); const isIssueIndex = page === 'projects:issues:index'; - const isMRIndex = (page === page && page === 'projects:merge_requests:index'); - const isSelecting = (selected.name !== selectedMilestone); + const isMRIndex = page === page && page === 'projects:merge_requests:index'; + const 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(); return; } @@ -177,10 +186,13 @@ export default class MilestoneSelect { return $dropdown.closest('form').submit(); } else if ($dropdown.hasClass('js-issue-board-sidebar')) { if (selected.id !== -1 && isSelecting) { - gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ - id: selected.id, - title: selected.name - })); + gl.issueBoards.boardStoreIssueSet( + 'milestone', + new ListMilestone({ + id: selected.id, + title: selected.name, + }), + ); } else { gl.issueBoards.boardStoreIssueDelete('milestone'); } @@ -188,7 +200,8 @@ export default class MilestoneSelect { $dropdown.trigger('loading.gl.dropdown'); $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(() => { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); @@ -203,7 +216,8 @@ export default class MilestoneSelect { data[abilityName].milestone_id = selected != null ? selected : null; $loading.removeClass('hidden').fadeIn(); $dropdown.trigger('loading.gl.dropdown'); - return axios.put(issueUpdateURL, data) + return axios + .put(issueUpdateURL, data) .then(({ data }) => { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); @@ -215,7 +229,10 @@ export default class MilestoneSelect { data.milestone.name = data.milestone.title; $value.html(milestoneLinkTemplate(data.milestone)); return $sidebarCollapsedValue - .attr('data-original-title', `${data.milestone.name}
${data.milestone.remaining}`) + .attr( + 'data-original-title', + `${data.milestone.name}
${data.milestone.remaining}`, + ) .find('span') .text(data.milestone.title); } else { @@ -230,7 +247,7 @@ export default class MilestoneSelect { $loading.fadeOut(); }); } - } + }, }); }); } diff --git a/changelogs/unreleased/winh-dashboard-any-milestone.yml b/changelogs/unreleased/winh-dashboard-any-milestone.yml new file mode 100644 index 0000000000000000000000000000000000000000..49eecd3da2b5930a35c871d2bfeac3c60ddb09f5 --- /dev/null +++ b/changelogs/unreleased/winh-dashboard-any-milestone.yml @@ -0,0 +1,5 @@ +--- +title: Reset milestone filter when clicking "Any Milestone" in dashboard +merge_request: 18531 +author: +type: fixed diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb index c965b565ca3f1dabf3223bdc9d44fa042341a79e..8cd57f4f32754a3526d17cf0d26c14ddc010f77d 100644 --- a/spec/features/dashboard/milestone_filter_spec.rb +++ b/spec/features/dashboard/milestone_filter_spec.rb @@ -10,13 +10,16 @@ feature 'Dashboard > milestone filter', :js do let!(:issue) { create :issue, author: user, project: project, milestone: milestone } let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 } + dropdown_toggle_button = '.js-milestone-select' + before do sign_in(user) - visit issues_dashboard_path(author_id: user.id) end context 'default state' do it 'shows issues with Any Milestone' do + visit issues_dashboard_path(author_id: user.id) + page.all('.issue-info').each do |issue_info| expect(issue_info.text).to match(/v\d.0/) end @@ -24,31 +27,51 @@ feature 'Dashboard > milestone filter', :js do end context 'filtering by milestone' do - milestone_select_selector = '.js-milestone-select' - before do - filter_item_select('v1.0', milestone_select_selector) - find(milestone_select_selector).click + visit issues_dashboard_path(author_id: user.id) + filter_item_select('v1.0', dropdown_toggle_button) + find(dropdown_toggle_button).click wait_for_requests end it 'shows issues with Milestone v1.0' do 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 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) + + find('.dropdown-menu-close').click - # open & close dropdown - find('.dropdown-menu-close').click + expect(page).not_to have_selector('.dropdown.open') + + find(dropdown_toggle_button).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 + + 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 - expect(find('.milestone-filter')).not_to have_selector('.dropdown.open') + it 'removes milestone filter from URL after clicking "Any Milestone"' do + expect(current_url).to include("milestone_title=#{milestone.title}") - find(milestone_select_selector).click + find('.milestone-filter .dropdown-content li', text: 'Any Milestone').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') + expect(current_url).not_to include('milestone_title') end end end