diff options
author | Fatih Acet <acetfatih@gmail.com> | 2016-09-01 21:32:44 +0000 |
---|---|---|
committer | Ruben Davila <rdavila84@gmail.com> | 2016-09-05 19:05:39 -0500 |
commit | d950556be80154f7a2e12521e8e86b41cc1e212d (patch) | |
tree | c20827b7674db38279ca2d6a97b97b7484db75b7 | |
parent | d4aff2e7836c006c7b7c759eb11f505422b1bab6 (diff) | |
download | gitlab-ce-d950556be80154f7a2e12521e8e86b41cc1e212d.tar.gz |
Merge branch 'issue-boards-issues-total-count' into 'master'
Add the total number of issues in the JSON response in issue board lists
Add the total number of issues in the JSON response in issue board lists
The issue board lists should always show the total number of issues in the list, not the current amount fetched by endless scroll.
Closes #21327
See merge request !5904
Conflicts:
app/assets/stylesheets/pages/boards.scss
app/views/projects/boards/components/_board.html.haml
-rw-r--r-- | app/assets/javascripts/boards/components/board_list.js.es6 | 13 | ||||
-rw-r--r-- | app/assets/javascripts/boards/models/list.js.es6 | 25 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/boards.scss | 24 | ||||
-rw-r--r-- | app/controllers/projects/boards/issues_controller.rb | 15 | ||||
-rw-r--r-- | app/views/projects/boards/components/_board.html.haml | 14 | ||||
-rw-r--r-- | spec/features/boards/boards_spec.rb | 21 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/issues.json | 15 | ||||
-rw-r--r-- | spec/javascripts/boards/mock_data.js.es6 | 15 |
8 files changed, 103 insertions, 39 deletions
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6 index a6644e9eb8c..50fc11d7737 100644 --- a/app/assets/javascripts/boards/components/board_list.js.es6 +++ b/app/assets/javascripts/boards/components/board_list.js.es6 @@ -20,7 +20,8 @@ data () { return { scrollOffset: 250, - filters: Store.state.filters + filters: Store.state.filters, + showCount: false }; }, watch: { @@ -30,6 +31,15 @@ this.$els.list.scrollTop = 0; }, deep: true + }, + issues () { + this.$nextTick(() => { + if (this.scrollHeight() > this.listHeight()) { + this.showCount = true; + } else { + this.showCount = false; + } + }); } }, methods: { @@ -58,6 +68,7 @@ group: 'issues', sort: false, disabled: this.disabled, + filter: '.board-list-count', onStart: (e) => { const card = this.$refs.issue[e.oldIndex]; diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6 index be2b8c568a8..d4e755b9b70 100644 --- a/app/assets/javascripts/boards/models/list.js.es6 +++ b/app/assets/javascripts/boards/models/list.js.es6 @@ -11,6 +11,7 @@ class List { this.loading = true; this.loadingMore = false; this.issues = []; + this.issuesSize = 0; if (obj.label) { this.label = new ListLabel(obj.label); @@ -51,7 +52,7 @@ class List { } nextPage () { - if (Math.floor(this.issues.length / 20) === this.page) { + if (this.issuesSize > this.issues.length) { this.page++; return this.getIssues(false); @@ -80,12 +81,13 @@ class List { .then((resp) => { const data = resp.json(); this.loading = false; + this.issuesSize = data.size; if (emptyIssues) { this.issues = []; } - this.createIssues(data); + this.createIssues(data.issues); }); } @@ -96,14 +98,20 @@ class List { } addIssue (issue, listFrom) { - this.issues.push(issue); + if (!this.findIssue(issue.id)) { + this.issues.push(issue); - if (this.label) { - issue.addLabel(this.label); - } + if (this.label) { + issue.addLabel(this.label); + } - if (listFrom) { - gl.boardService.moveIssue(issue.id, listFrom.id, this.id); + if (listFrom) { + this.issuesSize++; + gl.boardService.moveIssue(issue.id, listFrom.id, this.id) + .then(() => { + listFrom.getIssues(false); + }); + } } } @@ -116,6 +124,7 @@ class List { const matchesRemove = removeIssue.id === issue.id; if (matchesRemove) { + this.issuesSize--; issue.removeLabel(this.label); } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 9ac4d801ac4..f8e44ba7cb3 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -142,11 +142,6 @@ } } -.board-header-loading-spinner { - margin-right: 10px; - color: $gray-darkest; -} - .board-inner-container { border-bottom: 1px solid $border-color; padding: $gl-padding; @@ -304,3 +299,22 @@ margin-right: 8px; font-weight: 500; } + +.issue-boards-search { + width: 335px; + + .form-control { + display: inline-block; + width: 210px; + } +} + +.board-list-count { + padding: 10px 0; + color: $gl-placeholder-color; + font-size: 13px; + + > .fa { + margin-right: 5px; + } +} diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb index 1a4f6b50e8f..9404612a993 100644 --- a/app/controllers/projects/boards/issues_controller.rb +++ b/app/controllers/projects/boards/issues_controller.rb @@ -8,12 +8,15 @@ module Projects issues = ::Boards::Issues::ListService.new(project, current_user, filter_params).execute issues = issues.page(params[:page]) - render json: issues.as_json( - only: [:iid, :title, :confidential], - include: { - assignee: { only: [:id, :name, :username], methods: [:avatar_url] }, - labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] } - }) + render json: { + issues: issues.as_json( + only: [:iid, :title, :confidential], + include: { + assignee: { only: [:id, :name, :username], methods: [:avatar_url] }, + labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] } + }), + size: issues.total_count + } end def update diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml index de53a298f84..73066150fb3 100644 --- a/app/views/projects/boards/components/_board.html.haml +++ b/app/views/projects/boards/components/_board.html.haml @@ -13,19 +13,13 @@ %h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" } {{ list.title }} %span.pull-right{ "v-if" => "list.type !== 'blank'" } - {{ list.issues.length }} + {{ list.issuesSize }} - if can?(current_user, :admin_list, @project) %board-delete{ "inline-template" => true, ":list" => "list", "v-if" => "!list.preset && list.id" } %button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" } = icon("trash") - = icon("spinner spin", class: "board-header-loading-spinner pull-right", "v-show" => "list.loadingMore") - .board-inner-container.board-search-container{ "v-if" => "list.canSearch()" } - %input.form-control{ type: "text", placeholder: "Search issues", "v-model" => "query", "debounce" => "250" } - = icon("search", class: "board-search-icon", "v-show" => "!query") - %button.board-search-clear-btn{ type: "button", role: "button", "aria-label" => "Clear search", "@click" => "query = ''", "v-show" => "query" } - = icon("times", class: "board-search-clear") %board-list{ "inline-template" => true, "v-if" => "list.type !== 'blank'", ":list" => "list", @@ -39,5 +33,11 @@ "v-show" => "!loading", ":data-board" => "list.id" } = render "projects/boards/components/card" + %li.board-list-count.text-center{ "v-if" => "showCount" } + = icon("spinner spin", "v-show" => "list.loadingMore" ) + %span{ "v-if" => "list.issues.length === list.issuesSize" } + Showing all issues + %span{ "v-else" => true } + Showing {{ list.issues.length }} of {{ list.issuesSize }} issues - if can?(current_user, :admin_list, @project) = render "projects/boards/components/blank_state" diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 5d777895542..b4193060a88 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -143,14 +143,21 @@ describe 'Issue Boards', feature: true, js: true do wait_for_vue_resource page.within(find('.board', match: :first)) do - expect(page.find('.board-header')).to have_content('20') + expect(page.find('.board-header')).to have_content('56') expect(page).to have_selector('.card', count: 20) + expect(page).to have_content('Showing 20 of 56 issues') evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight") wait_for_vue_resource(spinner: false) - expect(page.find('.board-header')).to have_content('40') expect(page).to have_selector('.card', count: 40) + expect(page).to have_content('Showing 40 of 56 issues') + + evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight") + wait_for_vue_resource(spinner: false) + + expect(page).to have_selector('.card', count: 56) + expect(page).to have_content('Showing all issues') end end @@ -466,13 +473,19 @@ describe 'Issue Boards', feature: true, js: true do wait_for_vue_resource page.within(find('.board', match: :first)) do - expect(page.find('.board-header')).to have_content('20') + expect(page.find('.board-header')).to have_content('51') expect(page).to have_selector('.card', count: 20) + expect(page).to have_content('Showing 20 of 51 issues') evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight") - expect(page.find('.board-header')).to have_content('40') expect(page).to have_selector('.card', count: 40) + expect(page).to have_content('Showing 40 of 51 issues') + + evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight") + + expect(page).to have_selector('.card', count: 51) + expect(page).to have_content('Showing all issues') end end diff --git a/spec/fixtures/api/schemas/issues.json b/spec/fixtures/api/schemas/issues.json index 0d2067f704a..70771b21c96 100644 --- a/spec/fixtures/api/schemas/issues.json +++ b/spec/fixtures/api/schemas/issues.json @@ -1,4 +1,15 @@ { - "type": "array", - "items": { "$ref": "issue.json" } + "type": "object", + "required" : [ + "issues", + "size" + ], + "properties" : { + "issues": { + "type": "array", + "items": { "$ref": "issue.json" } + }, + "size": { "type": "integer" } + }, + "additionalProperties": false } diff --git a/spec/javascripts/boards/mock_data.js.es6 b/spec/javascripts/boards/mock_data.js.es6 index 0c37ec8354f..f3797ed44d4 100644 --- a/spec/javascripts/boards/mock_data.js.es6 +++ b/spec/javascripts/boards/mock_data.js.es6 @@ -26,12 +26,15 @@ const listObjDuplicate = { const BoardsMockData = { 'GET': { - '/test/issue-boards/board/lists{/id}/issues': [{ - title: 'Testing', - iid: 1, - confidential: false, - labels: [] - }] + '/test/issue-boards/board/lists{/id}/issues': { + issues: [{ + title: 'Testing', + iid: 1, + confidential: false, + labels: [] + }], + size: 1 + } }, 'POST': { '/test/issue-boards/board/lists{/id}': listObj |