summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2017-06-07 15:05:37 +0000
committerDouwe Maan <douwe@gitlab.com>2017-06-07 15:05:37 +0000
commit0bcb1d35ecb7db43a075c0a4ae9782991f724f1a (patch)
tree5ef82ca038eb3cb97e0eb351f33eb58261c3c536 /app
parenta5757c72d967acd82865f9f7cb288d5b61b0b35c (diff)
parent1633d3d7d8b8589a3d04358d6473cfd168633a10 (diff)
downloadgitlab-ce-0bcb1d35ecb7db43a075c0a4ae9782991f724f1a.tar.gz
Merge branch 'expand-backlog-closed-lists-issue-boards' into 'master'
Expand/collapse close & backlog lists in issue boards Closes #23917 See merge request !11820
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js4
-rw-r--r--app/assets/javascripts/boards/components/board.js23
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.js1
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js3
-rw-r--r--app/assets/javascripts/boards/components/modal/lists_dropdown.js2
-rw-r--r--app/assets/javascripts/boards/models/list.js4
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js8
-rw-r--r--app/assets/stylesheets/pages/boards.scss42
-rw-r--r--app/controllers/projects/boards/lists_controller.rb4
-rw-r--r--app/models/board.rb4
-rw-r--r--app/models/list.rb2
-rw-r--r--app/services/boards/create_service.rb1
-rw-r--r--app/services/boards/issues/list_service.rb2
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--app/views/projects/boards/_show.html.haml1
-rw-r--r--app/views/projects/boards/components/_board.html.haml11
16 files changed, 100 insertions, 14 deletions
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 0e4aa39226b..b94009ee76b 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -88,6 +88,8 @@ $(() => {
if (list.type === 'closed') {
list.position = Infinity;
list.label = { description: 'Shows all closed issues. Moving an issue to this list closes it' };
+ } else if (list.type === 'backlog') {
+ list.position = -1;
}
});
@@ -128,7 +130,7 @@ $(() => {
},
computed: {
disabled() {
- return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
+ return !this.store.lists.filter(list => !list.preset).length;
},
tooltipTitle() {
if (this.disabled) {
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 9ba84489910..adb7360327c 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,6 +1,7 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
/* global Sortable */
import Vue from 'vue';
+import AccessorUtilities from '../../lib/utils/accessor';
import boardList from './board_list';
import boardBlankState from './board_blank_state';
import './board_delete';
@@ -22,6 +23,10 @@ gl.issueBoards.Board = Vue.extend({
disabled: Boolean,
issueLinkBase: String,
rootPath: String,
+ boardId: {
+ type: String,
+ required: true,
+ },
},
data () {
return {
@@ -78,7 +83,16 @@ gl.issueBoards.Board = Vue.extend({
methods: {
showNewIssueForm() {
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
- }
+ },
+ toggleExpanded(e) {
+ if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
+ this.list.isExpanded = !this.list.isExpanded;
+
+ if (AccessorUtilities.isLocalStorageAccessSafe()) {
+ localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
+ }
+ }
+ },
},
mounted () {
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
@@ -102,4 +116,11 @@ gl.issueBoards.Board = Vue.extend({
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
},
+ created() {
+ if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
+ const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
+
+ this.list.isExpanded = !isCollapsed;
+ }
+ },
});
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index 4699ef5a51c..daef01bc93d 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -152,6 +152,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
<div class="card-assignee">
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
+ :key="assignee.id"
v-if="shouldRenderAssignee(index)"
class="js-no-trigger"
:link-href="assigneeUrl(assignee)"
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index fe7ab2db85d..478a1335b2b 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -26,7 +26,8 @@ gl.issueBoards.ModalFooter = Vue.extend({
},
methods: {
addIssues() {
- const list = this.modal.selectedList || this.state.lists[0];
+ const firstListIndex = 1;
+ const list = this.modal.selectedList || this.state.lists[firstListIndex];
const selectedIssues = ModalStore.getSelectedIssues();
const issueIds = selectedIssues.map(issue => issue.globalId);
diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.js b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
index 8cd15df90fa..4684ea76647 100644
--- a/app/assets/javascripts/boards/components/modal/lists_dropdown.js
+++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
@@ -11,7 +11,7 @@ gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
},
computed: {
selected() {
- return this.modal.selectedList || this.state.lists[0];
+ return this.modal.selectedList || this.state.lists[1];
},
},
destroyed() {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 8514e7df1d3..548de1a4c52 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -12,7 +12,9 @@ class List {
this.position = obj.position;
this.title = obj.title;
this.type = obj.list_type;
- this.preset = ['closed', 'blank'].indexOf(this.type) > -1;
+ this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1;
+ this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
+ this.isExpanded = true;
this.page = 1;
this.loading = true;
this.loadingMore = false;
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 7bddcdc3c1d..1e12d4ca415 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -32,10 +32,14 @@ gl.issueBoards.BoardsStore = {
},
new (listObj) {
const list = this.addList(listObj);
+ const backlogList = this.findList('type', 'backlog', 'backlog');
list
.save()
.then(() => {
+ // Remove any new issues from the backlog
+ // as they will be visible in the new list
+ list.issues.forEach(backlogList.removeIssue.bind(backlogList));
this.state.lists = _.sortBy(this.state.lists, 'position');
})
.catch(() => {
@@ -48,7 +52,7 @@ gl.issueBoards.BoardsStore = {
},
shouldAddBlankState () {
// Decide whether to add the blank state
- return !(this.state.lists.filter(list => list.type !== 'closed')[0]);
+ return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
},
addBlankState () {
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
@@ -101,7 +105,7 @@ gl.issueBoards.BoardsStore = {
issueTo.removeLabel(listFrom.label);
}
- if (listTo.type === 'closed') {
+ if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
issueLists.forEach((list) => {
list.removeIssue(issue);
});
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index e3e94c8ca50..740e383dbb5 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -96,9 +96,51 @@
@media (min-width: $screen-sm-min) {
width: 400px;
}
+
+ &.is-expandable {
+ .board-header {
+ cursor: pointer;
+ }
+ }
+
+ &.is-collapsed {
+ width: 50px;
+
+ .board-header {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ .board-title {
+ position: initial;
+ padding: 0;
+ border-bottom: 0;
+
+ > span {
+ display: block;
+ transform: rotate(90deg) translate(25px, 0);
+ }
+ }
+
+ .board-title-expandable-toggle {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-left: -10px;
+ }
+
+ .board-list-component,
+ .board-issue-count-holder {
+ display: none;
+ }
+ }
}
.board-inner {
+ position: relative;
height: 100%;
font-size: $issue-boards-font-size;
background: $gray-light;
diff --git a/app/controllers/projects/boards/lists_controller.rb b/app/controllers/projects/boards/lists_controller.rb
index 67e3c9add81..ad53bb749a0 100644
--- a/app/controllers/projects/boards/lists_controller.rb
+++ b/app/controllers/projects/boards/lists_controller.rb
@@ -5,7 +5,9 @@ module Projects
before_action :authorize_read_list!, only: [:index]
def index
- render json: serialize_as_json(board.lists)
+ lists = ::Boards::Lists::ListService.new(project, current_user).execute(board)
+
+ render json: serialize_as_json(lists)
end
def create
diff --git a/app/models/board.rb b/app/models/board.rb
index cf8317891b5..18081a32157 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -5,6 +5,10 @@ class Board < ActiveRecord::Base
validates :project, presence: true
+ def backlog_list
+ lists.merge(List.backlog).take
+ end
+
def closed_list
lists.merge(List.closed).take
end
diff --git a/app/models/list.rb b/app/models/list.rb
index ba7353a1325..918275be142 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -2,7 +2,7 @@ class List < ActiveRecord::Base
belongs_to :board
belongs_to :label
- enum list_type: { label: 1, closed: 2 }
+ enum list_type: { backlog: 0, label: 1, closed: 2 }
validates :board, :list_type, presence: true
validates :label, :position, presence: true, if: :label?
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index fd9ff115eab..68f6a8619e5 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -12,6 +12,7 @@ module Boards
def create_board!
board = project.boards.create
+ board.lists.create(list_type: :backlog)
board.lists.create(list_type: :closed)
board
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 533e6787855..418fa9afd6e 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -3,7 +3,7 @@ module Boards
class ListService < BaseService
def execute
issues = IssuesFinder.new(current_user, filter_params).execute
- issues = without_board_labels(issues) unless list
+ issues = without_board_labels(issues) unless movable_list?
issues = with_list_label(issues) if movable_list?
issues.order_by_position_and_priority
end
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index c579ed4c869..df2a01a69e5 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -2,6 +2,8 @@ module Boards
module Lists
class ListService < BaseService
def execute(board)
+ board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
+
board.lists
end
end
diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml
index efec69662f3..6684ecfce81 100644
--- a/app/views/projects/boards/_show.html.haml
+++ b/app/views/projects/boards/_show.html.haml
@@ -26,6 +26,7 @@
":disabled" => "disabled",
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
+ ":board-id" => "boardId",
":key" => "_uid" }
= render "projects/boards/components/sidebar"
%board-add-issues-modal{ "blank-state-image" => render('shared/empty_states/icons/issues.svg'),
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index bc5c727bf0d..55c4d51be14 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -1,8 +1,11 @@
-.board{ ":class" => '{ "is-draggable": !list.preset }',
+.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded }',
":data-id" => "list.id" }
.board-inner
- %header.board-header{ ":class" => '{ "has-border": list.label }', ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
+ %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
%h3.board-title.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset) }' }
+ %i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
+ ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded && list.position === -1, \"fa-caret-left\": !list.isExpanded && list.position !== -1 }",
+ "aria-hidden": "true" }
%span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")',
data: { container: "body", placement: "bottom" } }
{{ list.title }}
@@ -10,13 +13,13 @@
%span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
{{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project)
- %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
+ %button.btn.btn-small.btn-default.pull-right.has-tooltip.js-no-trigger-collapse{ type: "button",
"@click" => "showNewIssueForm",
"v-if" => 'list.type !== "closed"',
"aria-label" => "New issue",
"title" => "New issue",
data: { placement: "top", container: "body" } }
- = icon("plus")
+ = icon("plus", class: "js-no-trigger-collapse")
- if can?(current_user, :admin_list, @project)
%board-delete{ "inline-template" => true,
":list" => "list",