Merge pull request #5003 from opf/feature/23993/relation-toggle

[23993] Relation toggler
pull/5005/head
Markus Kahl 8 years ago committed by GitHub
commit 89f9e93cfd
  1. 2
      config/locales/js-en.yml
  2. 1
      frontend/app/components/wp-relations/wp-relation-row/wp-relation-row.directive.ts
  3. 6
      frontend/app/components/wp-relations/wp-relation-row/wp-relation-row.template.html
  4. 34
      frontend/app/components/wp-relations/wp-relations-group/wp-relations-group.directive.ts
  5. 13
      frontend/app/components/wp-relations/wp-relations-group/wp-relations-group.template.html
  6. 22
      frontend/app/components/wp-relations/wp-relations.directive.ts
  7. 4
      frontend/app/components/wp-relations/wp-relations.template.html
  8. 68
      spec/features/work_packages/details/details_relations_spec.rb

@ -285,6 +285,8 @@ en:
relation_buttons:
change_parent: "Change parent"
remove_parent: "Remove parent"
group_by_wp_type: "Group by work package type"
group_by_relation_type: "Group by relation type"
add_parent: "Add existing parent"
add_new_child: "Create new child"
add_existing_child: "Add existing child"

@ -161,6 +161,7 @@ function WpRelationRowDirective() {
templateUrl:'/components/wp-relations/wp-relation-row/wp-relation-row.template.html',
scope:{
workPackage: '=',
groupByWorkPackageType: '=',
relatedWorkPackage: '='
},
controller:WpRelationRowDirectiveController,

@ -8,7 +8,11 @@
<span class="relation-row--type"
ng-click="$ctrl.userInputs.showRelationTypesForm = true"
ng-if="!$ctrl.userInputs.showRelationTypesForm">
{{ $ctrl.normalizedRelationType }}
<span ng-if="$ctrl.groupByWorkPackageType"
ng-bind="$ctrl.normalizedRelationType"></span>
<span ng-if="!$ctrl.groupByWorkPackageType"
ng-bind="$ctrl.relatedWorkPackage.type.name"></span>
</span>
<div class="wp-edit-field inplace-edit" ng-if="$ctrl.userInputs.showRelationTypesForm">
<select class="wp-inline-edit--field form--select"

@ -29,12 +29,33 @@
import {wpDirectivesModule} from '../../../angular-modules';
import {RelatedWorkPackage} from '../wp-relations.interfaces';
import {WorkPackageResourceInterface} from '../../api/api-v3/hal-resources/work-package-resource.service';
import {WorkPackageRelationsController} from '../wp-relations.directive';
export class WorkPackageRelationsGroupController {
public relatedWorkPackages:Array<RelatedWorkPackage>;
public workPackage:WorkPackageResourceInterface;
public wpType:string;
public header:string;
public firstGroup:boolean;
public groupByWorkPackageType:boolean;
public text:Object;
public relationsCtrl: WorkPackageRelationsController;
constructor(public $element:ng.IAugmentedJQuery,
public $timeout:ng.ITimeoutService,
public I18n:op.I18n) {
this.text = {
groupByType: I18n.t('js.relation_buttons.group_by_wp_type'),
groupByRelation: I18n.t('js.relation_buttons.group_by_relation_type')
};
}
public toggleButton() {
this.relationsCtrl.toggleGroupBy();
this.$timeout(() => {
this.$element.find('#wp-relation-group-by-toggle').focus();
});
}
}
function wpRelationsGroupDirective() {
@ -43,13 +64,22 @@ function wpRelationsGroupDirective() {
templateUrl: '/components/wp-relations/wp-relations-group/wp-relations-group.template.html',
scope: {
wpType: '=',
header: '=',
firstGroup: '=',
workPackage: '=',
groupByWorkPackageType: '=',
relatedWorkPackages: '='
},
link: (scope,
element,
attrs,
controllers: [WorkPackageRelationsController]) => {
scope.$ctrl.relationsCtrl = controllers[0];
},
controller: WorkPackageRelationsGroupController,
controllerAs: '$ctrl',
require: ['^wpRelations'],
bindToController: true,
};
}

@ -2,14 +2,25 @@
<div class="attributes-group--header">
<div class="attributes-group--header-container">
<h3 class="attributes-group--header-text relation-group--header">
{{ $ctrl.wpType }}
{{ $ctrl.header }}
</h3>
</div>
<div class="attributes-group--header-toggle" ng-if="$ctrl.firstGroup">
<div id="wp-relation-group-by-toggle"
class="panel-toggler ng-scope ng-isolate-scope">
<accessible-by-keyboard link-class="icon-context icon-small icon-group-by button -transparent"
execute="$ctrl.toggleButton()">
<span ng-if="!$ctrl.groupByWorkPackageType" ng-bind="::$ctrl.text.groupByType"></span>
<span ng-if="$ctrl.groupByWorkPackageType" ng-bind="::$ctrl.text.groupByRelation"></span>
</accessible-by-keyboard>
</div>
</div>
</div>
<div class="content" ng-if="$ctrl.relatedWorkPackages">
<wp-relation-row
work-package="$ctrl.workPackage"
group-by-work-package-type="$ctrl.groupByWorkPackageType"
related-work-package="relatedWorkPackage"
ng-repeat="relatedWorkPackage in $ctrl.relatedWorkPackages"></wp-relation-row>
</div>

@ -40,11 +40,14 @@ export class WorkPackageRelationsController {
public workPackage:WorkPackageResourceInterface;
public canAddRelation:boolean = !!this.workPackage.addRelation;
// By default, group by relation type
public groupByWorkPackageType = false;
public currentRelations: RelatedWorkPackage[] = [];
constructor(protected $scope:ng.IScope,
protected $q:ng.IQService,
protected $state:ng.ui.IState,
protected I18n:op.I18n,
protected wpCacheService:WorkPackageCacheService) {
this.registerEventListeners();
@ -68,7 +71,6 @@ export class WorkPackageRelationsController {
this.buildRelationGroups();
}
protected getRelatedWorkPackages(workPackageIds:number[]) {
let observablesToGetZipped = workPackageIds.map(wpId => this.wpCacheService.loadWorkPackage(wpId).observe(this.$scope));
@ -86,10 +88,24 @@ export class WorkPackageRelationsController {
return parseInt(relation[direction].href.split('/').pop());
}
public toggleGroupBy() {
this.groupByWorkPackageType = !this.groupByWorkPackageType;
this.buildRelationGroups();
}
protected buildRelationGroups() {
if (angular.isDefined(this.currentRelations)) {
this.relationGroups = <RelatedWorkPackagesGroup> _.groupBy(this.currentRelations, (wp) => wp.type.name);
if (!angular.isDefined(this.currentRelations)) {
return;
}
this.relationGroups = <RelatedWorkPackagesGroup> _.groupBy(this.currentRelations, (wp) => {
if (this.groupByWorkPackageType) {
return wp.type.name;
} else {
var normalizedType = wp.relatedBy.normalizedType(this.workPackage);
return this.I18n.t('js.relation_labels.' + normalizedType);
}
});
}
protected addSingleRelation(evt, relation) {

@ -1,6 +1,8 @@
<div>
<div ng-repeat="(type, relatedWorkPackages) in $ctrl.relationGroups">
<wp-relations-group wp-type="type"
<wp-relations-group header="type"
group-by-work-package-type="$ctrl.groupByWorkPackageType"
first-group="$first"
related-work-packages="relatedWorkPackages"
work-package="$ctrl.workPackage"></wp-relations-group>
</div>

@ -9,9 +9,17 @@ describe 'Work package relations tab', js: true, selenium: true do
let(:work_package) { FactoryGirl.create(:work_package, project: project) }
let(:work_packages_page) { ::Pages::SplitWorkPackage.new(work_package) }
let(:visit) { true }
before do
login_as user
if visit
visit_relations
end
end
def visit_relations
work_packages_page.visit_tab!('relations')
work_packages_page.expect_subject
loading_indicator_saveguard
@ -41,7 +49,6 @@ describe 'Work package relations tab', js: true, selenium: true do
# Labels to expect
relation_label = I18n.t('js.relation_labels.' + relation_type)
type_upcase = wp.type.name.upcase
select relation_label, from: 'relation-type--select'
@ -52,10 +59,10 @@ describe 'Work package relations tab', js: true, selenium: true do
container.find('.wp-create-relation--save').click
expect(page).to have_selector('.relation-group--header',
text: type_upcase,
text: relation_label.upcase,
wait: 10)
expect(page).to have_selector('.relation-row--type', text: relation_label)
expect(page).to have_selector('.relation-row--type', text: wp.type.name)
expect(page).to have_selector('.wp-relations--subject-field', text: wp.subject)
@ -106,6 +113,58 @@ describe 'Work package relations tab', js: true, selenium: true do
end
end
describe 'relation group-by toggler' do
let(:project) { FactoryGirl.create :project, types: [type_1, type_2] }
let(:type_1) { FactoryGirl.create :type }
let(:type_2) { FactoryGirl.create :type }
let(:to_1) { FactoryGirl.create(:work_package, type: type_1, project: project) }
let(:to_2) { FactoryGirl.create(:work_package, type: type_2, project: project) }
let!(:relation_1) do
FactoryGirl.create :relation,
from: work_package,
to: to_1,
relation_type: :follows
end
let!(:relation_2) do
FactoryGirl.create :relation,
from: work_package,
to: to_2,
relation_type: :relates
end
let(:toggle_btn_selector) { '#wp-relation-group-by-toggle' }
let(:visit) { false }
it 'allows to toggle how relations are grouped' do
visit_relations
work_packages_page.visit_tab!('relations')
work_packages_page.expect_subject
loading_indicator_saveguard
# Expect to be grouped by relation type by default
expect(page).to have_selector(toggle_btn_selector,
text: 'Group by work package type', wait: 10)
expect(page).to have_selector('.relation-group--header', text: 'FOLLOWS')
expect(page).to have_selector('.relation-group--header', text: 'RELATED TO')
expect(page).to have_selector('.relation-row--type', text: type_1.name)
expect(page).to have_selector('.relation-row--type', text: type_2.name)
find(toggle_btn_selector).click
expect(page).to have_selector(toggle_btn_selector, text: 'Group by relation type', wait: 10)
expect(page).to have_selector('.relation-group--header', text: type_1.name.upcase)
expect(page).to have_selector('.relation-group--header', text: type_2.name.upcase)
expect(page).to have_selector('.relation-row--type', text: 'Follows')
expect(page).to have_selector('.relation-row--type', text: 'Related To')
end
end
describe 'with limited permissions' do
let(:permissions) { %i(view_work_packages) }
let(:user_role) do
@ -200,8 +259,7 @@ describe 'Work package relations tab', js: true, selenium: true do
created_row.hover
created_row.find('.relation-row--remove-btn').click
expect(page).to have_no_selector('.relation-group--header',
text: relatable.type.name.upcase)
expect(page).to have_no_selector('.relation-group--header', text: 'FOLLOWS')
expect(page).to have_no_selector('.wp-relations--subject-field', text: relatable.subject)
work_package.reload

Loading…
Cancel
Save