From c18d499b0ec471217e641633d73c9e41ce876557 Mon Sep 17 00:00:00 2001 From: Coung Ngo Date: Tue, 16 Mar 2021 19:46:19 +0000 Subject: [PATCH 1/2] Add sorting functionality to Vue issues list page This MR adds sorting functionality to the Vue issues list page, which is under the feature flag `vue_issues_list` defaulted to off. --- .../components/issuable_list_root.vue | 28 ++- .../components/issuables_list_app.vue | 3 + .../components/issues_list_app.vue | 74 ++++++- .../javascripts/issues_list/constants.js | 188 ++++++++++++++++++ app/assets/javascripts/issues_list/index.js | 2 + .../filtered_search_bar_root.vue | 2 +- app/views/projects/issues/index.html.haml | 3 +- locale/gitlab.pot | 18 ++ .../components/issuable_list_root_spec.js | 88 ++++++-- .../components/issues_list_app_spec.js | 149 ++++++++++++-- 10 files changed, 514 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue index 40b0fcbb8c6eec..1b54ba766ff3f1 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue @@ -10,7 +10,14 @@ import IssuableBulkEditSidebar from './issuable_bulk_edit_sidebar.vue'; import IssuableItem from './issuable_item.vue'; import IssuableTabs from './issuable_tabs.vue'; +const VueDraggable = () => import('vuedraggable'); + export default { + vueDraggableAttributes: { + animation: 200, + ghostClass: 'gl-visibility-hidden', + tag: 'ul', + }, components: { GlSkeletonLoading, IssuableTabs, @@ -18,6 +25,7 @@ export default { IssuableItem, IssuableBulkEditSidebar, GlPagination, + VueDraggable, }, props: { namespace: { @@ -127,6 +135,11 @@ export default { required: false, default: null, }, + isManualOrdering: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -159,6 +172,9 @@ export default { return acc; }, []); }, + issuablesWrapper() { + return this.isManualOrdering ? VueDraggable : 'ul'; + }, }, watch: { issuables(list) { @@ -208,6 +224,9 @@ export default { this.checkedIssuables[issuableId].checked = value; }); }, + handleVueDraggableUpdate({ newIndex, oldIndex }) { + this.$emit('reorder', { newIndex, oldIndex }); + }, }, }; @@ -253,13 +272,18 @@ export default { - + sortParams[key].order_by === orderBy && sortParams[key].sort === sort, + ); + return { currentPage: toNumber(getParameterByName('page')) || 1, + filters: sortParams[sortKey] || {}, isLoading: false, issues: [], + sortKey: sortKey || CREATED_DESC, totalIssues: 0, }; }, @@ -42,8 +65,12 @@ export default { return { page: this.currentPage, state: IssuableStatus.Open, + ...this.filters, }; }, + isManualOrdering() { + return this.sortKey === RELATIVE_POSITION_ASC; + }, }, mounted() { this.fetchIssues(); @@ -59,6 +86,7 @@ export default { per_page: this.$options.PAGE_SIZE, state: IssuableStatus.Open, with_labels_details: true, + ...this.filters, }, }) .then(({ data, headers }) => { @@ -76,6 +104,44 @@ export default { handlePageChange(page) { this.fetchIssues(page); }, + handleReorder({ newIndex, oldIndex }) { + const issueToMove = this.issues[oldIndex]; + const isDragDropDownwards = newIndex > oldIndex; + const isMovingToBeginning = newIndex === 0; + const isMovingToEnd = newIndex === this.issues.length - 1; + + let moveBeforeId; + let moveAfterId; + + if (isDragDropDownwards) { + const afterIndex = isMovingToEnd ? newIndex : newIndex + 1; + moveBeforeId = this.issues[newIndex].id; + moveAfterId = this.issues[afterIndex].id; + } else { + const beforeIndex = isMovingToBeginning ? newIndex : newIndex - 1; + moveBeforeId = this.issues[beforeIndex].id; + moveAfterId = this.issues[newIndex].id; + } + + return axios + .put(`${this.issuesPath}/${issueToMove.iid}/reorder`, { + move_before_id: isMovingToBeginning ? null : moveBeforeId, + move_after_id: isMovingToEnd ? null : moveAfterId, + }) + .then(() => { + // Move issue to new position in list + this.issues.splice(oldIndex, 1); + this.issues.splice(newIndex, 0, issueToMove); + }) + .catch(() => { + createFlash({ message: this.$options.i18n.reorderError }); + }); + }, + handleSort(value) { + this.sortKey = value; + this.filters = sortParams[value]; + this.fetchIssues(); + }, }, }; @@ -86,11 +152,13 @@ export default { recent-searches-storage-key="issues" :search-input-placeholder="__('Search or filter results…')" :search-tokens="[]" - :sort-options="[]" + :sort-options="$options.sortOptions" + :initial-sort-by="sortKey" :issuables="issues" :tabs="[]" current-tab="" :issuables-loading="isLoading" + :is-manual-ordering="isManualOrdering" :show-pagination-controls="true" :total-items="totalIssues" :current-page="currentPage" @@ -98,6 +166,8 @@ export default { :next-page="currentPage + 1" :url-params="urlParams" @page-change="handlePageChange" + @reorder="handleReorder" + @sort="handleSort" >