diff --git a/app/assets/javascripts/angular/directives/work_packages/column-context-menu-directive.js b/app/assets/javascripts/angular/directives/work_packages/column-context-menu-directive.js
deleted file mode 100644
index 08b2cf0fce..0000000000
--- a/app/assets/javascripts/angular/directives/work_packages/column-context-menu-directive.js
+++ /dev/null
@@ -1,72 +0,0 @@
-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.isGroupable = WorkPackagesTableService.isGroupable(scope.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) {
- ContextMenuService.close();
- QueryService.hideColumns(new Array(columnName));
- };
- }
- };
-}]);
diff --git a/app/assets/javascripts/angular/directives/work_packages/sort-header-directive.js b/app/assets/javascripts/angular/directives/work_packages/sort-header-directive.js
index 00ad98974d..271ac30223 100644
--- a/app/assets/javascripts/angular/directives/work_packages/sort-header-directive.js
+++ b/app/assets/javascripts/angular/directives/work_packages/sort-header-directive.js
@@ -28,12 +28,13 @@
angular.module('openproject.workPackages.directives')
-.directive('sortHeader', ['I18n', function(I18n){
+.directive('sortHeader', [
+ 'I18n',
+ function(I18n){
return {
restrict: 'A',
templateUrl: '/templates/work_packages/sort_header.html',
- transclude: true,
scope: {
query: '=',
headerName: '=',
@@ -41,7 +42,8 @@ angular.module('openproject.workPackages.directives')
sortable: '=',
locale: '='
},
- link: function(scope, element, attributes) {
+ require: 'hasDropdownMenu',
+ link: function(scope, element, attributes, dropdownMenuCtrl) {
scope.$watch('query.sortation.sortElements', function(sortElements){
var latestSortElement = sortElements[0];
@@ -54,17 +56,6 @@ angular.module('openproject.workPackages.directives')
setFullTitle();
}, true);
- scope.$watch('currentSortDirection', function(sort) {
- element.toggleClass('active-column', !!sort);
- });
-
- scope.performSort = function(){
- var targetSortation = scope.query.sortation.getTargetSortationOfHeader(scope.headerName);
-
- scope.query.setSortation(targetSortation);
- scope.currentSortDirection = scope.query.sortation.getDisplayedSortDirectionOfHeader(scope.headerName);
- };
-
function setFullTitle() {
if(!scope.sortable) scope.fullTitle = '';
@@ -72,9 +63,21 @@ angular.module('openproject.workPackages.directives')
var sortDirectionText = (scope.currentSortDirection == 'asc') ? I18n.t('js.label_ascending') : I18n.t('js.label_descending');
scope.fullTitle = sortDirectionText + " " + I18n.t('js.label_sorted_by') + ' \"' + scope.headerTitle + '\"';
} else {
- scope.fullTitle = (I18n.t('js.label_sort_by') + ' \"' + scope.headerTitle + '\"');
+ scope.fullTitle = I18n.t('js.label_open_menu');
}
}
+
+ // active-column class setting
+
+ function setActiveColumnClass() {
+ element.toggleClass('active-column', !!scope.currentSortDirection || scope.dropDownMenuOpened);
+ }
+ scope.$watch(dropdownMenuCtrl.opened, function(opened) {
+ scope.dropDownMenuOpened = opened;
+ setActiveColumnClass();
+ });
+ scope.$watch('currentSortDirection', setActiveColumnClass);
+
}
};
}]);
diff --git a/app/assets/javascripts/angular/directives/work_packages/work-package-context-menu-directive.js b/app/assets/javascripts/angular/directives/work_packages/work-package-context-menu-directive.js
deleted file mode 100644
index c286e4d3dc..0000000000
--- a/app/assets/javascripts/angular/directives/work_packages/work-package-context-menu-directive.js
+++ /dev/null
@@ -1,106 +0,0 @@
-angular.module('openproject.workPackages.directives')
-
-.directive('workPackageContextMenu', [
- 'ContextMenuService',
- 'WorkPackagesTableHelper',
- 'WorkPackageContextMenuHelper',
- 'WorkPackageService',
- 'WorkPackagesTableService',
- 'I18n',
- '$window',
- function(ContextMenuService, WorkPackagesTableHelper, WorkPackageContextMenuHelper, WorkPackageService, WorkPackagesTableService, I18n, $window) {
- return {
- restrict: 'EA',
- replace: true,
- scope: {},
- templateUrl: '/templates/work_packages/work_package_context_menu.html',
- link: function(scope, element, attrs) {
- var contextMenuName = 'workPackageContextMenu';
-
- scope.I18n = I18n;
-
- scope.hideResourceActions = true;
-
- // wire up context menu event handler
- 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;
- });
-
- scope.$watch('contextMenu.context.row', function(row) {
- if (row && scope.contextMenu.targetMenu === contextMenuName) {
- updateContextMenu(getWorkPackagesFromContext(scope.contextMenu.context));
- }
- });
-
- scope.triggerContextMenuAction = function(action, link) {
- if (action === 'delete') {
- deleteSelectedWorkPackages();
- } else {
- $window.location.href = link;
- }
- };
-
- function deleteSelectedWorkPackages() {
- if (!deleteConfirmed()) return;
-
- var rows = WorkPackagesTableHelper.getSelectedRows(scope.contextMenu.context.rows);
-
- WorkPackageService.performBulkDelete(getWorkPackagesFromContext(scope.contextMenu.context))
- .success(function(data, status) {
- // TODO wire up to API and processs API response
- scope.$emit('flashMessage', {
- isError: false,
- text: I18n.t('js.work_packages.message_successful_bulk_delete')
- });
-
- WorkPackagesTableService.removeRows(rows);
- })
- .error(function(data, status) {
- // TODO wire up to API and processs API response
- scope.$emit('flashMessage', {
- isError: true,
- text: I18n.t('js.work_packages.message_error_during_bulk_delete')
- });
- });
- }
-
- function deleteConfirmed() {
- return $window.confirm(I18n.t('js.text_work_packages_destroy_confirmation'));
- }
-
- function updateContextMenu(workPackages) {
- scope.permittedActions = WorkPackageContextMenuHelper.getPermittedActions(workPackages);
- }
-
- function getWorkPackagesFromSelectedRows(rows) {
- var selectedRows = WorkPackagesTableHelper.getSelectedRows(rows);
-
- return WorkPackagesTableHelper.getWorkPackagesFromRows(selectedRows);
- }
-
- function getWorkPackagesFromContext(context) {
- if (!context.row) return [];
-
- context.row.checked = true;
-
- var workPackagefromContext = context.row.object;
- var workPackagesfromSelectedRows = getWorkPackagesFromSelectedRows(context.rows);
-
- if (workPackagesfromSelectedRows.length === 0) {
- return [workPackagefromContext];
- } else if (workPackagesfromSelectedRows.indexOf(workPackagefromContext) === -1) {
- return [workPackagefromContext].concat(workPackagesfromSelectedRows);
- } else {
- return workPackagesfromSelectedRows;
- }
- }
-
- }
- };
-}]);
diff --git a/app/assets/javascripts/angular/openproject-app.js b/app/assets/javascripts/angular/openproject-app.js
index e402932f98..60f7ba3bcf 100644
--- a/app/assets/javascripts/angular/openproject-app.js
+++ b/app/assets/javascripts/angular/openproject-app.js
@@ -67,7 +67,8 @@ angular.module('openproject.workPackages', [
'openproject.workPackages.controllers',
'openproject.workPackages.filters',
'openproject.workPackages.directives',
- 'openproject.uiComponents'
+ 'openproject.uiComponents',
+ 'ng-context-menu'
]);
angular.module('openproject.workPackages.services', []);
angular.module('openproject.workPackages.helpers', [
@@ -88,8 +89,7 @@ angular.module('openproject.workPackages.controllers', [
angular.module('openproject.workPackages.directives', [
'openproject.uiComponents',
'openproject.services',
- 'openproject.workPackages.services',
- 'ng-context-menu'
+ 'openproject.workPackages.services'
]);
// messages
diff --git a/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js
new file mode 100644
index 0000000000..c3ad1f56b5
--- /dev/null
+++ b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js
@@ -0,0 +1,131 @@
+angular.module('openproject.uiComponents')
+
+.directive('hasDropdownMenu', [
+ '$injector',
+ '$window',
+ '$parse',
+ function($injector, $window, $parse) {
+
+ function getCssPositionProperties(dropdown, trigger) {
+ var hOffset = 0,
+ vOffset = 0;
+
+ // Styling logic taken from jQuery-dropdown plugin: https://github.com/plapier/jquery-dropdown
+ // (dual MIT/GPL-Licensed)
+
+ // Position the dropdown relative-to-parent or relative-to-document
+ if (dropdown.hasClass('dropdown-relative')) {
+ return {
+ left: dropdown.hasClass('dropdown-anchor-right') ?
+ trigger.position().left - (dropdown.outerWidth(true) - trigger.outerWidth(true)) - parseInt(trigger.css('margin-right')) + hOffset :
+ trigger.position().left + parseInt(trigger.css('margin-left')) + hOffset,
+ top: trigger.position().top + trigger.outerHeight(true) - parseInt(trigger.css('margin-top')) + vOffset
+ };
+ } else {
+ return {
+ left: dropdown.hasClass('dropdown-anchor-right') ?
+ trigger.offset().left - (dropdown.outerWidth() - trigger.outerWidth()) + hOffset : trigger.offset().left + hOffset,
+ top: trigger.offset().top + trigger.outerHeight() + vOffset
+ };
+ }
+ }
+
+ return {
+ restrict: 'A',
+ controller: [function() {
+ var dropDownMenuOpened = false;
+
+ this.open = function() {
+ dropDownMenuOpened = true;
+ };
+ this.close = function() {
+ dropDownMenuOpened = false;
+ };
+ this.opened = function() {
+ return dropDownMenuOpened;
+ };
+ }],
+ link: function(scope, element, attrs, ctrl) {
+ var contextMenu = $injector.get(attrs.target),
+ locals = {},
+ win = angular.element($window),
+ menuElement,
+ triggerOnEvent = attrs.triggerOnEvent || 'click';
+
+ /* contextMenu is a mandatory attribute and used to bind a specific context
+ menu to the trigger event
+ triggerOnEvent allows for binding the event for opening the menu to "click" */
+
+ // prepare locals, these define properties to be passed on to the context menu scope
+ var localKeys = attrs.locals.split(',').map(function(local) {
+ return local.trim();
+ });
+ angular.forEach(localKeys, function(key) {
+ locals[key] = scope[key];
+ });
+
+ function toggle() {
+ active() ? close() : open();
+ }
+
+ function active() {
+ return contextMenu.active() && ctrl.opened();
+ }
+
+ function open() {
+ ctrl.open();
+
+ contextMenu.open(locals)
+ .then(function(menuElement) {
+ menuElement.css(getCssPositionProperties(menuElement, element));
+ });
+ }
+
+ function close() {
+ ctrl.close();
+
+ contextMenu.close();
+ }
+
+ element.bind(triggerOnEvent, function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ scope.$apply(function() {
+ toggle();
+ });
+
+ scope.$root.$broadcast('openproject.markDropdownsAsClosed', element);
+ });
+
+ scope.$on('openproject.markDropdownsAsClosed', function(event, target) {
+ if (element !== target && ctrl.opened()) {
+ scope.$apply(ctrl.close);
+ }
+ });
+
+
+ win.bind('keyup', function(event) {
+ if (contextMenu.active() && event.keyCode === 27) {
+ scope.$apply(function() {
+ close();
+ });
+ }
+ });
+
+ function handleWindowClickEvent(event) {
+ if (contextMenu.active() && event.button !== 2) {
+
+ scope.$apply(function() {
+ close();
+ });
+ }
+ }
+
+ // Firefox treats a right-click as a click and a contextmenu event while other browsers
+ // just treat it as a contextmenu event
+ win.bind('click', handleWindowClickEvent);
+ win.bind(triggerOnEvent, handleWindowClickEvent);
+ }
+ };
+}]);
diff --git a/app/assets/javascripts/angular/work_packages/column-context-menu.js b/app/assets/javascripts/angular/work_packages/column-context-menu.js
new file mode 100644
index 0000000000..74224a4b66
--- /dev/null
+++ b/app/assets/javascripts/angular/work_packages/column-context-menu.js
@@ -0,0 +1,85 @@
+//-- 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.
+//++
+
+angular.module('openproject.workPackages')
+
+.factory('ColumnContextMenu', [
+ 'ngContextMenu',
+ function(ngContextMenu) {
+
+ return ngContextMenu({
+ controller: 'ColumnContextMenuController',
+ controllerAs: 'contextMenu',
+ templateUrl: '/templates/work_packages/column_context_menu.html'
+ });
+}])
+
+.controller('ColumnContextMenuController', [
+ '$scope',
+ 'ColumnContextMenu',
+ 'I18n',
+ 'QueryService',
+ 'WorkPackagesTableHelper',
+ 'WorkPackagesTableService',
+ 'columnsModal',
+ function($scope, ColumnContextMenu, I18n, QueryService, WorkPackagesTableHelper, WorkPackagesTableService, columnsModal) {
+
+ $scope.I18n = I18n;
+ $scope.isGroupable = WorkPackagesTableService.isGroupable($scope.column);
+
+ // context menu actions
+
+ $scope.groupBy = function(columnName) {
+ QueryService.getQuery().groupBy = columnName;
+ };
+
+ $scope.sortAscending = function(columnName) {
+ WorkPackagesTableService.sortBy(columnName || 'id', 'asc');
+ };
+
+ $scope.sortDescending = function(columnName) {
+ WorkPackagesTableService.sortBy(columnName || 'id', '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) {
+ ColumnContextMenu.close();
+ QueryService.hideColumns(new Array(columnName));
+ };
+
+ $scope.insertColumns = function() {
+ columnsModal.activate();
+ };
+}]);
diff --git a/app/assets/javascripts/angular/work_packages/work-package-context-menu.js b/app/assets/javascripts/angular/work_packages/work-package-context-menu.js
new file mode 100644
index 0000000000..a878384391
--- /dev/null
+++ b/app/assets/javascripts/angular/work_packages/work-package-context-menu.js
@@ -0,0 +1,117 @@
+//-- 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.
+//++
+
+angular.module('openproject.workPackages')
+
+.factory('WorkPackageContextMenu', [
+ 'ngContextMenu',
+ function(ngContextMenu) {
+
+ return ngContextMenu({
+ controller: 'WorkPackageContextMenuController',
+ controllerAs: 'contextMenu',
+ templateUrl: '/templates/work_packages/work_package_context_menu.html'
+ });
+}])
+
+.controller('WorkPackageContextMenuController', [
+ '$scope',
+ 'WorkPackagesTableHelper',
+ 'WorkPackageContextMenuHelper',
+ 'WorkPackageService',
+ 'WorkPackagesTableService',
+ 'I18n',
+ '$window',
+ function($scope, WorkPackagesTableHelper, WorkPackageContextMenuHelper, WorkPackageService, WorkPackagesTableService, I18n, $window) {
+
+ $scope.I18n = I18n;
+
+ $scope.hideResourceActions = true;
+
+ $scope.$watch('row', function() {
+ $scope.row.checked = true;
+ $scope.permittedActions = WorkPackageContextMenuHelper.getPermittedActions(getSelectedWorkPackages());
+ });
+
+
+ $scope.triggerContextMenuAction = function(action, link) {
+ if (action === 'delete') {
+ deleteSelectedWorkPackages();
+ } else {
+ $window.location.href = link;
+ }
+ };
+
+ function deleteSelectedWorkPackages() {
+ if (!deleteConfirmed()) return;
+
+ var rows = WorkPackagesTableHelper.getSelectedRows($scope.rows);
+
+ WorkPackageService.performBulkDelete(getSelectedWorkPackages())
+ .success(function(data, status) {
+ // TODO wire up to API and processs API response
+ $scope.$emit('flashMessage', {
+ isError: false,
+ text: I18n.t('js.work_packages.message_successful_bulk_delete')
+ });
+
+ WorkPackagesTableService.removeRows(rows);
+ })
+ .error(function(data, status) {
+ // TODO wire up to API and processs API response
+ $scope.$emit('flashMessage', {
+ isError: true,
+ text: I18n.t('js.work_packages.message_error_during_bulk_delete')
+ });
+ });
+ }
+
+ function deleteConfirmed() {
+ return $window.confirm(I18n.t('js.text_work_packages_destroy_confirmation'));
+ }
+
+ function getWorkPackagesFromSelectedRows() {
+ var selectedRows = WorkPackagesTableHelper.getSelectedRows($scope.rows);
+
+ return WorkPackagesTableHelper.getWorkPackagesFromRows(selectedRows);
+ }
+
+ function getSelectedWorkPackages() {
+ var workPackagefromContext = $scope.row.object;
+ var workPackagesfromSelectedRows = getWorkPackagesFromSelectedRows();
+
+ if (workPackagesfromSelectedRows.length === 0) {
+ return [workPackagefromContext];
+ } else if (workPackagesfromSelectedRows.indexOf(workPackagefromContext) === -1) {
+ return [workPackagefromContext].concat(workPackagesfromSelectedRows);
+ } else {
+ return workPackagesfromSelectedRows;
+ }
+ }
+
+}]);
diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb
index ddb3472e8b..e3c89db917 100644
--- a/app/assets/javascripts/application.js.erb
+++ b/app/assets/javascripts/application.js.erb
@@ -66,7 +66,7 @@
//= require angular-truncate
//= require angular-feature-flags/dist/featureFlags.js
-//= require ng-context-menu
+//= require angular-context-menu
//= require openproject-ui_components/app/assets/javascripts/angular/ui-components-app
//= require ./angular/openproject-app
diff --git a/app/assets/stylesheets/content/_context_menu.sass b/app/assets/stylesheets/content/_context_menu.sass
index dd4aabf581..e24c053831 100644
--- a/app/assets/stylesheets/content/_context_menu.sass
+++ b/app/assets/stylesheets/content/_context_menu.sass
@@ -37,6 +37,7 @@
#work-package-context-menu, #column-context-menu
&.action-menu
position: absolute
+ z-index: 1000
.hascontextmenu
cursor: context-menu
diff --git a/app/assets/stylesheets/layout/_drop_down.sass b/app/assets/stylesheets/layout/_drop_down.sass
index ef732e9226..2033d0932b 100644
--- a/app/assets/stylesheets/layout/_drop_down.sass
+++ b/app/assets/stylesheets/layout/_drop_down.sass
@@ -36,6 +36,9 @@
#settingsDropdown, #tasksDropdown
margin: 10px 0 0 0
+#column-context-menu
+ margin: 55px 0 0 0
+
.dropdown
position: absolute
z-index: 9999999
diff --git a/bower.json b/bower.json
index 92c712fc6d..b564140c32 100644
--- a/bower.json
+++ b/bower.json
@@ -22,7 +22,7 @@
"jquery-migrate": "~1.2.1",
"momentjs": "~2.6.0",
"moment-timezone": "~0.0.6",
- "ng-context-menu": "finnlabs/ng-context-menu#context-sharing-with-multiple-targets"
+ "angular-context-menu": "0.1.1"
},
"devDependencies": {
"mocha": "~1.14.0",
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 9f307aca8e..350c3e52c2 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -801,6 +801,7 @@ de:
label_not_contains: "enthält nicht"
label_not_equals: "ist nicht"
label_notify_member_plural: "Aktualisierungen per E-Mail verschicken"
+ label_open_menu: "Menü öffnen"
label_open_work_packages: "offen"
label_open_work_packages_plural: "offen"
label_optional_description: "Beschreibung"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bb11dcb6e7..a42d197d3b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -798,6 +798,7 @@ en:
label_not_contains: "doesn't contain"
label_not_equals: "is not"
label_notify_member_plural: "Email updates"
+ label_open_menu: "Open menu"
label_open_work_packages: "open"
label_open_work_packages_plural: "open"
label_optional_description: "Description"
diff --git a/config/locales/js-de.yml b/config/locales/js-de.yml
index 38500a4c2b..f2a7e38df5 100644
--- a/config/locales/js-de.yml
+++ b/config/locales/js-de.yml
@@ -217,6 +217,7 @@ de:
move_column_left: "Spalte nach links"
move_column_right: "Spalte nach rechts"
hide_column: "Spalte verbergen"
+ insert_columns: "Spalten hinzufügen ..."
filters: "Filter"
display_sums: "Summen anzeigen"
toolbar:
diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml
index 66ccbd8833..e26964c6da 100644
--- a/config/locales/js-en.yml
+++ b/config/locales/js-en.yml
@@ -220,6 +220,7 @@ en:
move_column_left: "Move column left"
move_column_right: "Move column right"
hide_column: "Hide column"
+ insert_columns: "Insert columns ..."
filters: "Filters"
display_sums: "Display Sums"
toolbar:
diff --git a/karma.conf.js b/karma.conf.js
index f11d4fdd40..290e045137 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -29,8 +29,8 @@ module.exports = function(config) {
"vendor/assets/components/angular-truncate/src/truncate.js",
"vendor/assets/components/angular-sanitize/angular-sanitize.js",
"vendor/assets/components/momentjs/moment.js",
+ "vendor/assets/components/angular-context-menu/dist/angular-context-menu.js",
'vendor/assets/components/select2/select2.js',
- "vendor/assets/components/ng-context-menu/dist/ng-context-menu.js",
"vendor/assets/components/openproject-ui_components/app/assets/javascripts/angular/ui-components-app.js",
"app/assets/javascripts/angular/openproject-app.js",
@@ -102,6 +102,8 @@ module.exports = function(config) {
"app/assets/javascripts/angular/controllers/dialogs/share.js",
"app/assets/javascripts/angular/controllers/dialogs/sorting.js",
+ "app/assets/javascripts/angular/work_packages/**/*.js",
+
'app/assets/javascripts/autocompleter.js',
'app/assets/javascripts/members_select_boxes.js',
'app/assets/javascripts/openproject.js',
@@ -113,7 +115,7 @@ module.exports = function(config) {
'karma/lib/rosie.js',
'karma/tests/test-helper.js',
'karma/factories/*factory.js',
-
+
'vendor/assets/components/jquery-mockjax/jquery.mockjax.js',
'karma/tests/asset_functions.js',
diff --git a/karma/tests/directives/work_packages/sort-header-directive-test.js b/karma/tests/directives/work_packages/sort-header-directive-test.js
index ae855a1ef5..97f71606c3 100644
--- a/karma/tests/directives/work_packages/sort-header-directive-test.js
+++ b/karma/tests/directives/work_packages/sort-header-directive-test.js
@@ -43,9 +43,19 @@ describe('sortHeader Directive', function() {
rootScope = $rootScope;
scope = $rootScope.$new();
+ // Mock hasDropdownManu controller
+ var dropdownMenuController = function() {
+ this.open = function() {
+ return true;
+ };
+ };
+
compile = function() {
- $compile(element1)(scope);
- $compile(element2)(scope);
+ angular.forEach([element1, element2], function(element){
+ element.data('$hasDropdownMenuController', dropdownMenuController);
+ $compile(element)(scope);
+ });
+
scope.$digest();
};
}));
@@ -57,8 +67,10 @@ describe('sortHeader Directive', function() {
}));
describe('rendering multiple headers', function(){
+ var query;
+
beforeEach(function(){
- var query = new Query({
+ query = new Query({
});
query.setSortation(new Sortation([]));
scope.query = query;
@@ -93,10 +105,12 @@ describe('sortHeader Directive', function() {
var link1 = element1.find('span a').first();
expect(link1.hasClass('sort asc')).to.not.be.ok;
- link1.click();
+ query.sortation.addSortElement({ field: scope.headerName1, direction: 'asc' });
+ scope.$digest();
expect(link1.hasClass('sort asc')).to.be.ok;
- link1.click();
+ query.sortation.addSortElement({ field: scope.headerName1, direction: 'desc' });
+ scope.$digest();
expect(link1.hasClass('sort desc')).to.be.ok;
});
@@ -108,11 +122,15 @@ describe('sortHeader Directive', function() {
scope.$apply();
var link1 = element1.find('span a').first();
- link1.click();
+ query.sortation.addSortElement({ field: scope.headerName1, direction: 'asc' });
+ scope.$digest();
+
expect(link1.hasClass('sort asc')).to.be.ok;
var link2 = element2.find('span a').first();
- link2.click();
+ query.sortation.addSortElement({ field: scope.headerName2, direction: 'asc' });
+ scope.$digest();
+
expect(link2.hasClass('sort asc')).to.be.ok;
expect(link1.hasClass('sort asc')).to.not.be.ok;
});
diff --git a/karma/tests/directives/work_packages/column-context-menu-directive-test.js b/karma/tests/work_packages/column-context-menu-test.js
similarity index 62%
rename from karma/tests/directives/work_packages/column-context-menu-directive-test.js
rename to karma/tests/work_packages/column-context-menu-test.js
index e616235ea8..38e23f4c44 100644
--- a/karma/tests/directives/work_packages/column-context-menu-directive-test.js
+++ b/karma/tests/work_packages/column-context-menu-test.js
@@ -28,37 +28,36 @@
/*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 = '