Skip to content
Snippets Groups Projects
Commit 6f448bd1 authored by Rajat Jain's avatar Rajat Jain Committed by Kushal Pandya
Browse files

Bring Manual Ordering on Issue List

On all the issue lists -- Group, Project and Dashboard -- this
change adds a new option for managing the lists.

"Manual Ordering" option is added which when flipped on will allow
an user to drag and drop issues around to create a relative ordering
among them.
parent 58eae2d3
No related branches found
No related tags found
No related merge requests found
Showing
with 145 additions and 3 deletions
import Sortable from 'sortablejs';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import {
getBoardSortableDefaultOptions,
sortableStart,
} from '~/boards/mixins/sortable_default_options';
import axios from '~/lib/utils/axios_utils';
const updateIssue = (url, issueList, { move_before_id, move_after_id }) =>
axios
.put(`${url}/reorder`, {
move_before_id,
move_after_id,
group_full_path: issueList.dataset.groupFullPath,
})
.catch(() => {
createFlash(s__("ManualOrdering|Couldn't save the order of the issues"));
});
const initManualOrdering = () => {
const issueList = document.querySelector('.manual-ordering');
if (!issueList || !(gon.features && gon.features.manualSorting)) {
return;
}
Sortable.create(
issueList,
getBoardSortableDefaultOptions({
scroll: true,
dataIdAttr: 'data-id',
fallbackOnBody: false,
group: {
name: 'issues',
},
draggable: 'li.issue',
onStart: () => {
sortableStart();
},
onUpdate: event => {
const el = event.item;
const url = el.getAttribute('url');
const prev = el.previousElementSibling;
const next = el.nextElementSibling;
const beforeId = prev && parseInt(prev.dataset.id, 10);
const afterId = next && parseInt(next.dataset.id, 10);
updateIssue(url, issueList, { move_after_id: afterId, move_before_id: beforeId });
},
}),
);
};
export default initManualOrdering;
Loading
Loading
@@ -2,6 +2,7 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import initManualOrdering from '~/manual_ordering';
 
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
Loading
Loading
@@ -10,4 +11,5 @@ document.addEventListener('DOMContentLoaded', () => {
});
 
projectSelect();
initManualOrdering();
});
Loading
Loading
@@ -2,6 +2,7 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import initManualOrdering from '~/manual_ordering';
 
document.addEventListener('DOMContentLoaded', () => {
IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
Loading
Loading
@@ -12,4 +13,5 @@ document.addEventListener('DOMContentLoaded', () => {
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect();
initManualOrdering();
});
Loading
Loading
@@ -7,6 +7,7 @@ import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import initManualOrdering from '~/manual_ordering';
 
document.addEventListener('DOMContentLoaded', () => {
IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
Loading
Loading
@@ -19,4 +20,5 @@ document.addEventListener('DOMContentLoaded', () => {
 
new ShortcutsNavigation();
new UsersSelect();
initManualOrdering();
});
.issues-list {
&.manual-ordering {
background-color: $gray-light;
border-radius: $border-radius-default;
padding: $gl-padding-8;
.issue {
background-color: $white-light;
margin-bottom: $gl-padding-8;
border-radius: $border-radius-default;
border: 1px solid $gray-100;
box-shadow: 0 1px 2px $issue-boards-card-shadow;
}
}
.issue {
padding: 10px 0 10px $gl-padding;
position: relative;
Loading
Loading
Loading
Loading
@@ -7,6 +7,10 @@ class GroupsController < Groups::ApplicationController
include PreviewMarkdown
include RecordUserLastActivity
 
before_action do
push_frontend_feature_flag(:manual_sorting)
end
respond_to :html
 
prepend_before_action(only: [:show, :issues]) { authenticate_sessionless_user!(:rss) }
Loading
Loading
Loading
Loading
@@ -10,6 +10,10 @@ class Projects::IssuesController < Projects::ApplicationController
include SpammableActions
include RecordUserLastActivity
 
before_action do
push_frontend_feature_flag(:manual_sorting)
end
def issue_except_actions
%i[index calendar new create bulk_update import_csv]
end
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ module IssuesHelper
classes = ["issue"]
classes << "closed" if issue.closed?
classes << "today" if issue.today?
classes << "user-can-drag" if @sort == 'relative_position'
classes.join(' ')
end
 
Loading
Loading
- empty_state_path = local_assigns.fetch(:empty_state_path, 'shared/empty_states/issues')
 
%ul.content-list.issues-list.issuable-list
%ul.content-list.issues-list.issuable-list{ class: ("manual-ordering" if @sort == 'relative_position') }
= render partial: "projects/issues/issue", collection: @issues
- if @issues.blank?
= render empty_state_path
Loading
Loading
- if @issues.to_a.any?
.card.card-small.card-without-border
%ul.content-list.issues-list.issuable-list
%ul.content-list.issues-list.issuable-list{ class: ("manual-ordering" if @sort == 'relative_position'), data: { group_full_path: @group&.full_path } }
= render partial: 'projects/issues/issue', collection: @issues
= paginate @issues, theme: "gitlab"
- else
Loading
Loading
- sort_value = @sort
- sort_title = issuable_sort_option_title(sort_value)
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
- manual_sorting = viewing_issues && controller.controller_name != 'dashboard' && Feature.enabled?(:manual_sorting)
 
.dropdown.inline.prepend-left-10.issue-sort-dropdown
.btn-group{ role: 'group' }
Loading
Loading
@@ -17,6 +18,6 @@
= sortable_item(sort_title_due_date, page_filter_path(sort: sort_value_due_date), sort_title) if viewing_issues
= sortable_item(sort_title_popularity, page_filter_path(sort: sort_value_popularity), sort_title)
= sortable_item(sort_title_label_priority, page_filter_path(sort: sort_value_label_priority), sort_title)
= sortable_item(sort_title_relative_position, page_filter_path(sort: sort_value_relative_position), sort_title) if viewing_issues && Feature.enabled?(:manual_sorting)
= sortable_item(sort_title_relative_position, page_filter_path(sort: sort_value_relative_position), sort_title) if manual_sorting
= render_if_exists('shared/ee/issuable/sort_dropdown', viewing_issues: viewing_issues, sort_title: sort_title)
= issuable_sort_direction_button(sort_value)
---
title: Bring Manual Ordering on Issue List
merge_request: 29410
author:
type: added
Loading
Loading
@@ -6011,6 +6011,9 @@ msgstr ""
msgid "Manual job"
msgstr ""
 
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -2,6 +2,7 @@ require 'spec_helper'
 
describe 'Group issues page' do
include FilteredSearchHelpers
include DragTo
 
let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group)}
Loading
Loading
@@ -99,4 +100,49 @@ describe 'Group issues page' do
end
end
end
context 'manual ordering' do
let!(:issue1) { create(:issue, project: project, title: 'Issue #1') }
let!(:issue2) { create(:issue, project: project, title: 'Issue #2') }
let!(:issue3) { create(:issue, project: project, title: 'Issue #3') }
it 'displays all issues' do
visit issues_group_path(group, sort: 'relative_position')
page.within('.issues-list') do
expect(page).to have_selector('li.issue', count: 3)
end
end
it 'has manual-ordering css applied' do
visit issues_group_path(group, sort: 'relative_position')
expect(page).to have_selector('.manual-ordering')
end
it 'each issue item has a user-can-drag css applied' do
visit issues_group_path(group, sort: 'relative_position')
page.within('.manual-ordering') do
expect(page).to have_selector('.issue.user-can-drag', count: 3)
end
end
it 'issues should be draggable and persist order', :js do
visit issues_group_path(group, sort: 'relative_position')
drag_to(selector: '.manual-ordering',
scrollable: '#board-app',
list_from_index: 0,
from_index: 0,
to_index: 2,
list_to_index: 0)
page.within('.manual-ordering') do
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
expect(find('.issue:nth-child(2) .title')).to have_content('Issue #1')
expect(find('.issue:nth-child(3) .title')).to have_content('Issue #3')
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment