aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-06-04 12:20:31 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-06-04 13:57:43 +0200
commit7d6b2b569403ae8c960cce7f8f4f7a8842b24c63 (patch)
tree8d84e7658dc1ed0dc4ce96111bb9797b1859de71 /server/sonar-web/src/main/js
parent93e2db9c467f56954a1a2f33a45ab57a3cf24d75 (diff)
downloadsonarqube-7d6b2b569403ae8c960cce7f8f4f7a8842b24c63.tar.gz
sonarqube-7d6b2b569403ae8c960cce7f8f4f7a8842b24c63.zip
SONAR-5892 make it possible to select issues to bulk change
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/models/issues.js10
-rw-r--r--server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs26
-rw-r--r--server/sonar-web/src/main/js/apps/issues/workspace-header-view.js80
-rw-r--r--server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js29
-rw-r--r--server/sonar-web/src/main/js/apps/issues/workspace-list-view.js5
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs1
-rw-r--r--server/sonar-web/src/main/js/components/navigator/workspace-header-view.js7
9 files changed, 148 insertions, 15 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js b/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
index 6d8a6f73207..97d3e2b40e9 100644
--- a/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
@@ -5,7 +5,7 @@ define([
return IssueView.extend({
onRender: function () {
IssueView.prototype.onRender.apply(this, arguments);
- this.$el.removeClass('issue-navigate-right');
+ this.$el.removeClass('issue-navigate-right issue-with-checkbox');
},
serializeData: function () {
diff --git a/server/sonar-web/src/main/js/apps/issues/models/issues.js b/server/sonar-web/src/main/js/apps/issues/models/issues.js
index af41dd3fe12..2292dba2c8b 100644
--- a/server/sonar-web/src/main/js/apps/issues/models/issues.js
+++ b/server/sonar-web/src/main/js/apps/issues/models/issues.js
@@ -59,6 +59,16 @@ define([
return this.forEach(function (issue, index) {
return issue.set({ index: index });
});
+ },
+
+ selectByKeys: function (keys) {
+ var that = this;
+ keys.forEach(function (key) {
+ var issue = that.get(key);
+ if (issue) {
+ issue.set({ selected: true });
+ }
+ });
}
});
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs
new file mode 100644
index 00000000000..dbb50e24779
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs
@@ -0,0 +1,3 @@
+<div class="js-toggle issue-checkbox-container">
+ <i class="issue-checkbox icon-checkbox {{#if selected}}icon-checkbox-checked{{/if}}"></i>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs
index 2e2c9d2ff78..131af89407d 100644
--- a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs
+++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs
@@ -20,7 +20,11 @@
{{/with}}
</div>
{{else}}
- &nbsp;
+ {{#if state.canBulkChange}}
+ <a class="js-selection icon-checkbox {{#if allSelected}}icon-checkbox-checked{{/if}} {{#if someSelected}}icon-checkbox-checked icon-checkbox-single{{/if}}"></a>
+ {{else}}
+ &nbsp;
+ {{/if}}
{{/if}}
</div>
@@ -30,7 +34,11 @@
<div class="search-navigator-header-pagination">
{{#gt state.total 0}}
<a class="js-prev icon-prev" title="{{t "paging_previous"}}"></a>
- <span class="current">{{sum state.selectedIndex 1}} / <span id="issues-total">{{state.total}}</span></span>
+ <span class="current">
+ {{sum state.selectedIndex 1}}
+ /
+ <span id="issues-total">{{state.total}}</span>
+ </span>
<a class="js-next icon-next" title="{{t "paging_next"}}"></a>
{{else}}
<span class="current">0 / <span id="issues-total">0</span></span>
@@ -39,11 +47,21 @@
{{/notNull}}
- <div class="search-navigator-header-buttons button-group">
+ <div class="search-navigator-header-buttons button-group dropdown">
<button id="issues-reload" class="js-reload">{{t "reload"}}</button>
<button class="js-new-search" id="issues-new-search">{{t "issue_filter.new_search"}}</button>
{{#if state.canBulkChange}}
- <button id="issues-bulk-change" class="js-bulk-change">{{t "bulk_change"}}</button>
+ <button id="issues-bulk-change" class="dropdown-toggle" data-toggle="dropdown">{{t "bulk_change"}}</button>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a class="js-bulk-change" href="#">{{tp 'issues.bulk_change' state.total}}</a>
+ </li>
+ {{#gt selectedCount 0}}
+ <li>
+ <a class="js-bulk-change-selected" href="#">{{tp 'issues.bulk_change_selected' selectedCount}}</a>
+ </li>
+ {{/gt}}
+ </ul>
{{/if}}
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js
index 620b68bfccd..e7b317442b9 100644
--- a/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js
@@ -10,25 +10,73 @@ define([
events: function () {
return _.extend(WorkspaceHeaderView.prototype.events.apply(this, arguments), {
+ 'click .js-selection': 'onSelectionClick',
'click .js-back': 'returnToList',
- 'click .js-new-search': 'newSearch'
+ 'click .js-new-search': 'newSearch',
+ 'click .js-bulk-change-selected': 'onBulkChangeSelectedClick'
});
},
initialize: function () {
- var that = this;
WorkspaceHeaderView.prototype.initialize.apply(this, arguments);
this._onBulkIssues = window.onBulkIssues;
- window.onBulkIssues = function () {
- $('#modal').dialog('close');
- return that.options.app.controller.fetchList();
- };
+ window.onBulkIssues = _.bind(this.afterBulkChange, this);
},
onClose: function () {
window.onBulkIssues = this._onBulkIssues;
},
+ onSelectionClick: function (e) {
+ e.preventDefault();
+ this.toggleSelection();
+ },
+
+ onBulkChangeSelectedClick: function (e) {
+ e.preventDefault();
+ this.bulkChangeSelected();
+ },
+
+ afterBulkChange: function () {
+ var that = this;
+ $('#modal').dialog('close');
+ var selectedIndex = this.options.app.state.get('selectedIndex');
+ var selectedKeys = _.pluck(this.options.app.list.where({ selected: true }), 'id');
+ this.options.app.controller.fetchList().done(function () {
+ that.options.app.state.set({ selectedIndex: selectedIndex });
+ that.options.app.list.selectByKeys(selectedKeys);
+ });
+ },
+
+ render: function () {
+ if (!this._suppressUpdate) {
+ this._super();
+ }
+ },
+
+ toggleSelection: function () {
+ this._suppressUpdate = true;
+ var selectedCount = this.options.app.list.where({ selected: true }).length,
+ someSelected = selectedCount > 0;
+ return someSelected ? this.selectNone() : this.selectAll();
+ },
+
+ selectNone: function () {
+ this.options.app.list.where({ selected: true }).forEach(function (issue) {
+ issue.set({ selected: false });
+ });
+ this._suppressUpdate = false;
+ this.render();
+ },
+
+ selectAll: function () {
+ this.options.app.list.forEach(function (issue) {
+ issue.set({ selected: true });
+ });
+ this._suppressUpdate = false;
+ this.render();
+ },
+
returnToList: function () {
this.options.app.controller.closeComponentViewer();
},
@@ -41,6 +89,26 @@ define([
var query = this.options.app.controller.getQuery('&', true),
url = baseUrl + '/issues/bulk_change_form?' + query;
window.openModalWindow(url, {});
+ },
+
+ bulkChangeSelected: function () {
+ var selected = this.options.app.list.where({ selected: true }),
+ selectedKeys = _.first(_.pluck(selected, 'id'), 200),
+ query = 'issues=' + selectedKeys.join(),
+ url = baseUrl + '/issues/bulk_change_form?' + query;
+ window.openModalWindow(url, {});
+ },
+
+ serializeData: function () {
+ var issuesCount = this.options.app.list.length,
+ selectedCount = this.options.app.list.where({ selected: true }).length,
+ allSelected = issuesCount > 0 && issuesCount === selectedCount,
+ someSelected = !allSelected && selectedCount > 0;
+ return _.extend(this._super(), {
+ selectedCount: selectedCount,
+ allSelected: allSelected,
+ someSelected: someSelected
+ });
}
});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
index 002fafb0709..510f26c63ea 100644
--- a/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
@@ -16,6 +16,7 @@ define([
};
return IssueView.extend({
+ checkboxTemplate: Templates['issues-issue-checkbox'],
filterTemplate: Templates['issues-issue-filter'],
events: function () {
@@ -23,7 +24,8 @@ define([
'click': 'selectCurrent',
'dblclick': 'openComponentViewer',
'click .js-issue-navigate': 'openComponentViewer',
- 'click .js-issue-filter': 'onIssueFilterClick'
+ 'click .js-issue-filter': 'onIssueFilterClick',
+ 'click .js-toggle': 'onIssueToggle'
});
},
@@ -36,7 +38,11 @@ define([
IssueView.prototype.onRender.apply(this, arguments);
this.select();
this.addFilterSelect();
+ this.addCheckbox();
this.$el.addClass('issue-navigate-right');
+ if (this.options.app.state.get('canBulkChange')) {
+ this.$el.addClass('issue-with-checkbox');
+ }
},
onIssueFilterClick: function (e) {
@@ -67,12 +73,23 @@ define([
this.popup.render();
},
+ onIssueToggle: function (e) {
+ e.preventDefault();
+ this.model.set({ selected: !this.model.get('selected') });
+ var selected = this.model.collection.where({ selected: true }).length;
+ this.options.app.state.set({ selected: selected });
+ },
+
addFilterSelect: function () {
this.$('.issue-table-meta-cell-first')
.find('.issue-meta-list')
.append(this.filterTemplate(this.model.toJSON()));
},
+ addCheckbox: function () {
+ this.$el.append(this.checkboxTemplate(this.model.toJSON()));
+ },
+
select: function () {
var selected = this.model.get('index') === this.options.app.state.get('selectedIndex');
this.$el.toggleClass('selected', selected);
@@ -86,8 +103,14 @@ define([
var that = this;
var key = this.model.get('key'),
componentUuid = this.model.get('componentUuid'),
- index = this.model.get('index');
- this.model.reset({ key: key, componentUuid: componentUuid, index: index }, { silent: true });
+ index = this.model.get('index'),
+ selected = this.model.get('selected');
+ this.model.reset({
+ key: key,
+ componentUuid: componentUuid,
+ index: index,
+ selected: selected
+ }, { silent: true });
return this.model.fetch(options).done(function () {
return that.trigger('reset');
});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
index 37e7ade70e9..d4bdad5f65b 100644
--- a/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
@@ -32,6 +32,11 @@ define([
that.options.app.controller.showComponentViewer(selectedIssue);
return false;
});
+ key('space', 'list', function () {
+ var selectedIssue = that.collection.at(that.options.app.state.get('selectedIndex'));
+ selectedIssue.set({ selected: !selectedIssue.get('selected') });
+ return false;
+ });
key('f', 'list', function () {
return doAction('transition');
});
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
index 9457d1b7762..5230e7f5912 100644
--- a/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
@@ -40,6 +40,7 @@
</li>
<li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.open_details'}}</li>
<li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.return_to_list'}}</li>
+ <li><span class="shortcut-button">⎵</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.select'}}</li>
<li><span class="shortcut-button">f</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.do_transition'}}</li>
<li><span class="shortcut-button">a</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign'}}</li>
<li><span class="shortcut-button">m</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign_to_me'}}</li>
diff --git a/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js b/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js
index 071d51c69b2..bc4296fcb8a 100644
--- a/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js
+++ b/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js
@@ -29,7 +29,7 @@ define(function () {
events: function () {
return {
- 'click .js-bulk-change': 'bulkChange',
+ 'click .js-bulk-change': 'onBulkChangeClick',
'click .js-reload': 'reload',
'click .js-next': 'selectNext',
'click .js-prev': 'selectPrev'
@@ -40,6 +40,11 @@ define(function () {
this.listenTo(options.app.state, 'change', this.render);
},
+ onBulkChangeClick: function (e) {
+ e.preventDefault();
+ this.bulkChange();
+ },
+
bulkChange: function () {
},