Signed-off-by: Alex Coles <alex@alexbcoles.com> Conflicts: app/assets/javascripts/angular/openproject-app.js app/views/work_packages/_list.html.erbpull/1315/head
commit
f5f98e81de
@ -0,0 +1,70 @@ |
|||||||
|
angular.module('openproject.workPackages.directives') |
||||||
|
|
||||||
|
.directive('columnContextMenu', [ |
||||||
|
'ContextMenuService', |
||||||
|
'I18n', |
||||||
|
'QueryService', |
||||||
|
'WorkPackagesTableHelper', |
||||||
|
'WorkPackagesTableService', |
||||||
|
function(ContextMenuService, I18n, QueryService, WorkPackagesTableHelper, WorkPackagesTableService) { |
||||||
|
|
||||||
|
|
||||||
|
return { |
||||||
|
restrict: 'EA', |
||||||
|
replace: true, |
||||||
|
scope: {}, |
||||||
|
templateUrl: '/templates/work_packages/column_context_menu.html', |
||||||
|
link: function(scope, element, attrs) { |
||||||
|
var contextMenuName = 'columnContextMenu'; |
||||||
|
|
||||||
|
// Wire up context menu handlers
|
||||||
|
|
||||||
|
ContextMenuService.registerMenuElement(contextMenuName, element); |
||||||
|
scope.contextMenu = ContextMenuService.getContextMenu(); |
||||||
|
|
||||||
|
scope.$watch('contextMenu.opened', function(opened) { |
||||||
|
scope.opened = opened && scope.contextMenu.targetMenu === contextMenuName; |
||||||
|
}); |
||||||
|
scope.$watch('contextMenu.targetMenu', function(target) { |
||||||
|
scope.opened = scope.contextMenu.opened && target === contextMenuName; |
||||||
|
}); |
||||||
|
|
||||||
|
// shared context information
|
||||||
|
|
||||||
|
scope.$watch('contextMenu.context.column', function(column) { |
||||||
|
scope.column = column; |
||||||
|
}); |
||||||
|
scope.$watch('contextMenu.context.columns', function(columns) { |
||||||
|
scope.columns = columns; |
||||||
|
}); |
||||||
|
|
||||||
|
scope.I18n = I18n; |
||||||
|
|
||||||
|
// context menu actions
|
||||||
|
|
||||||
|
scope.groupBy = function(columnName) { |
||||||
|
QueryService.getQuery().groupBy = columnName; |
||||||
|
}; |
||||||
|
|
||||||
|
scope.sortAscending = function(columnName) { |
||||||
|
WorkPackagesTableService.sortBy(columnName, 'asc'); |
||||||
|
}; |
||||||
|
|
||||||
|
scope.sortDescending = function(columnName) { |
||||||
|
WorkPackagesTableService.sortBy(columnName, 'desc'); |
||||||
|
}; |
||||||
|
|
||||||
|
scope.moveLeft = function(columnName) { |
||||||
|
WorkPackagesTableHelper.moveColumnBy(scope.columns, columnName, -1); |
||||||
|
}; |
||||||
|
|
||||||
|
scope.moveRight = function(columnName) { |
||||||
|
WorkPackagesTableHelper.moveColumnBy(scope.columns, columnName, 1); |
||||||
|
}; |
||||||
|
|
||||||
|
scope.hideColumn = function(columnName) { |
||||||
|
QueryService.hideColumns(new Array(columnName)); |
||||||
|
}; |
||||||
|
} |
||||||
|
}; |
||||||
|
}]); |
@ -1 +1,51 @@ |
|||||||
# Action menu |
# Action menu |
||||||
|
|
||||||
|
``` |
||||||
|
<div class="action-menu"> |
||||||
|
<ul class="menu"> |
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-edit icon-actionmenu"></i>menu item for modal...</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-yes icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-copy icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li class="submenu-item"> |
||||||
|
<a href="#"><i class="icon-priority icon-actionmenu"></i>menu item with sub</a><i class="icon-pulldown-arrow4 icon-submenu"></i> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-delete icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li class="hasnoicon"> |
||||||
|
<a href="#">menu item no icon</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li class="dropdown-divider"></li> |
||||||
|
|
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-time icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
<div id="submenu" class="action-menu"> |
||||||
|
<ul class="menu"> |
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-edit icon-actionmenu"></i>menu item for modal...</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-yes icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a href="#"><i class="icon-copy icon-actionmenu"></i>menu item</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
``` |
||||||
|
@ -0,0 +1,137 @@ |
|||||||
|
//-- copyright
|
||||||
|
// OpenProject is a project management system.
|
||||||
|
// Copyright (C) 2012-2014 the OpenProject Foundation (OPF)
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License version 3.
|
||||||
|
//
|
||||||
|
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||||
|
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||||
|
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
// See doc/COPYRIGHT.rdoc for more details.
|
||||||
|
//++
|
||||||
|
|
||||||
|
/*jshint expr: true*/ |
||||||
|
|
||||||
|
describe('columnContextMenu Directive', function() { |
||||||
|
var compile, element, rootScope, scope; |
||||||
|
|
||||||
|
beforeEach(angular.mock.module('openproject.workPackages.directives')); |
||||||
|
beforeEach(module('templates', 'openproject.models')); |
||||||
|
|
||||||
|
beforeEach(inject(function($rootScope, $compile, _ContextMenuService_) { |
||||||
|
var html; |
||||||
|
html = '<column-context-menu></column-context-menu>'; |
||||||
|
|
||||||
|
element = angular.element(html); |
||||||
|
rootScope = $rootScope; |
||||||
|
scope = $rootScope.$new(); |
||||||
|
ContextMenuService = _ContextMenuService_; |
||||||
|
|
||||||
|
compile = function() { |
||||||
|
$compile(element)(scope); |
||||||
|
scope.$digest(); |
||||||
|
}; |
||||||
|
})); |
||||||
|
|
||||||
|
describe('element', function() { |
||||||
|
beforeEach(function() { |
||||||
|
compile(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render a surrounding div', function() { |
||||||
|
expect(element.prop('tagName')).to.equal('DIV'); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
describe('when the context menu handler of a column is clicked', function() { |
||||||
|
var I18n, QueryService; |
||||||
|
var column = { name: 'status', title: 'Status' }, |
||||||
|
anotherColumn = { name: 'subject', title: 'Subject' }, |
||||||
|
columns = [column, anotherColumn], |
||||||
|
query = Factory.build('Query', { columns: columns }); |
||||||
|
var directiveScope; |
||||||
|
|
||||||
|
beforeEach(inject(function(_QueryService_) { |
||||||
|
QueryService = _QueryService_; |
||||||
|
sinon.stub(QueryService, 'getQuery').returns(query); |
||||||
|
})); |
||||||
|
afterEach(inject(function() { |
||||||
|
QueryService.getQuery.restore(); |
||||||
|
})); |
||||||
|
|
||||||
|
beforeEach(function() { |
||||||
|
compile(); |
||||||
|
|
||||||
|
ContextMenuService.setContext({ column: column, columns: columns }); |
||||||
|
ContextMenuService.open('columnContextMenu'); |
||||||
|
scope.$apply(); |
||||||
|
|
||||||
|
directiveScope = element.children().scope(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('fetches the column from the context handle context', function() { |
||||||
|
expect(directiveScope.column).to.have.property('name').and.contain(column.name); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('and the group by option is clicked', function() { |
||||||
|
beforeEach(function() { |
||||||
|
directiveScope.groupBy(column.name); |
||||||
|
}); |
||||||
|
|
||||||
|
it('changes the query group by', function() { |
||||||
|
expect(query.groupBy).to.equal(column.name); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('and "move column right" is clicked', function() { |
||||||
|
beforeEach(function() { |
||||||
|
directiveScope.moveRight(column.name); |
||||||
|
}); |
||||||
|
|
||||||
|
it('moves the column right', function() { |
||||||
|
expect(columns[1]).to.equal(column); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('and "Sort ascending" is clicked', function() { |
||||||
|
var Sortation; |
||||||
|
|
||||||
|
beforeEach(inject(function(_Sortation_) { |
||||||
|
Sortation = _Sortation_; |
||||||
|
query.sortation = new Sortation(); |
||||||
|
directiveScope.sortAscending(column.name); |
||||||
|
})); |
||||||
|
|
||||||
|
it('updates the query sortation', function() { |
||||||
|
expect(query.sortation.getPrimarySortationCriterion()).to.deep.equal({ field: column.name, direction: 'asc' }); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('and "Hide column" is clicked', function() { |
||||||
|
beforeEach(function() { |
||||||
|
directiveScope.hideColumn(column.name); |
||||||
|
}); |
||||||
|
|
||||||
|
it('removes the column from the query columns', function() { |
||||||
|
expect(query.columns).to.not.include(column); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,28 @@ |
|||||||
|
<div id="column-context-menu" class="action-menu" ng-show="opened"> |
||||||
|
<ul class="menu"> |
||||||
|
<li ng-click="groupBy(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.work_packages.query.group_by')"/> {{column.title}}</a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li ng-click="sortAscending(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.label_sort_by')"/> <span ng-bind="column.title"/> <span ng-bind="I18n.t('js.label_ascending')"/></a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li ng-click="sortDescending(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.label_sort_by')"/> <span ng-bind="column.title"/> <span ng-bind="I18n.t('js.label_descending')"/></a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li ng-click="moveLeft(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.label_move_column_left')"/></a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li ng-click="moveRight(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.label_move_column_right')"/></a> |
||||||
|
</li> |
||||||
|
|
||||||
|
<li ng-click="hideColumn(column.name)"> |
||||||
|
<a href="#"><span ng-bind="I18n.t('js.label_hide_column')"/></a> |
||||||
|
</li> |
||||||
|
|
||||||
|
</ul> |
||||||
|
</div> |
@ -0,0 +1,40 @@ |
|||||||
|
require 'spec_helper' |
||||||
|
|
||||||
|
describe "layouts/base" do |
||||||
|
include Redmine::MenuManager::MenuHelper |
||||||
|
helper Redmine::MenuManager::MenuHelper |
||||||
|
let!(:user) { FactoryGirl.create :user } |
||||||
|
let!(:anonymous) { FactoryGirl.create(:anonymous) } |
||||||
|
|
||||||
|
before do |
||||||
|
view.stub(:current_menu_item).and_return("overview") |
||||||
|
view.stub(:default_breadcrumb) |
||||||
|
controller.stub(:default_search_scope) |
||||||
|
end |
||||||
|
|
||||||
|
describe "projects menu visibility" do |
||||||
|
context "when the user is not logged in" do |
||||||
|
before do |
||||||
|
User.stub(:current).and_return anonymous |
||||||
|
view.stub(:current_user).and_return anonymous |
||||||
|
render |
||||||
|
end |
||||||
|
|
||||||
|
it "the projects menu should not be displayed" do |
||||||
|
expect(response).to_not have_text("Projects") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context "when the user is logged in" do |
||||||
|
before do |
||||||
|
User.stub(:current).and_return user |
||||||
|
view.stub(:current_user).and_return user |
||||||
|
render |
||||||
|
end |
||||||
|
|
||||||
|
it "the projects menu should be displayed" do |
||||||
|
expect(response).to have_text("Projects") |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue