feat(context-pad): show delete only if allowed by rules

Related to bpmn-io/diagram-js#131
This commit is contained in:
Adam Brengesjö 2015-12-02 12:12:04 +01:00 committed by Nico Rehwaldt
parent eb75fda7e0
commit 9322a6a1e6
4 changed files with 166 additions and 16 deletions

View File

@ -3,6 +3,7 @@
var assign = require('lodash/object/assign'),
forEach = require('lodash/collection/forEach'),
isArray = require('lodash/lang/isArray'),
is = require('../../util/ModelUtil').is,
isAny = require('../modeling/util/ModelingUtil').isAny,
getChildLanes = require('../modeling/util/LaneUtil').getChildLanes,
@ -14,7 +15,7 @@ var assign = require('lodash/object/assign'),
*/
function ContextPadProvider(contextPad, modeling, elementFactory,
connect, create, bpmnReplace,
canvas) {
canvas, rules) {
contextPad.registerProvider(this);
@ -27,6 +28,7 @@ function ContextPadProvider(contextPad, modeling, elementFactory,
this._create = create;
this._bpmnReplace = bpmnReplace;
this._canvas = canvas;
this._rules = rules;
}
ContextPadProvider.$inject = [
@ -36,7 +38,8 @@ ContextPadProvider.$inject = [
'connect',
'create',
'bpmnReplace',
'canvas'
'canvas',
'rules'
];
module.exports = ContextPadProvider;
@ -51,7 +54,8 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
connect = this._connect,
create = this._create,
bpmnReplace = this._bpmnReplace,
canvas = this._canvas;
canvas = this._canvas,
rules = this._rules;
var actions = {};
@ -270,18 +274,27 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
});
}
// Delete Element Entry
assign(actions, {
'delete': {
group: 'edit',
className: 'bpmn-icon-trash',
title: 'Remove',
action: {
click: removeElement,
dragstart: removeElement
// delete element entry, only show if allowed by rules
var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ]});
if (isArray(deleteAllowed)) {
// was the element returned as a deletion candidate?
deleteAllowed = deleteAllowed[0] === element;
}
if (deleteAllowed) {
assign(actions, {
'delete': {
group: 'edit',
className: 'bpmn-icon-trash',
title: 'Remove',
action: {
click: removeElement,
dragstart: removeElement
}
}
}
});
});
}
return actions;
};

View File

@ -13,14 +13,15 @@ var contextPadModule = require('../../../../lib/features/context-pad'),
coreModule = require('../../../../lib/core'),
modelingModule = require('../../../../lib/features/modeling'),
popupModule = require('diagram-js/lib/features/popup-menu'),
replaceModule = require('diagram-js/lib/features/replace');
replaceModule = require('diagram-js/lib/features/replace'),
rulesModule = require('./rules');
describe('features - context-pad', function() {
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
var testModules = [ contextPadModule, coreModule, modelingModule, popupModule, replaceModule ];
var testModules = [ contextPadModule, coreModule, modelingModule, popupModule, replaceModule, rulesModule ];
beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
@ -34,6 +35,121 @@ describe('features - context-pad', function() {
});
describe('remove action rules', function () {
var deleteAction;
beforeEach(inject(function (contextPad) {
deleteAction = function(element) {
return domQuery('[data-action="delete"]', contextPad.getPad(element).html);
};
}));
it('should add delete action by default', inject(function (elementRegistry, contextPad) {
// given
var element = elementRegistry.get('StartEvent_1');
// when
contextPad.open(element);
// then
expect(deleteAction(element)).to.exist;
}));
it('should include delete action when rule returns true', inject(function (elementRegistry, contextPad, customRules) {
// given
customRules.addRule('elements.delete', function() {
return true;
});
var element = elementRegistry.get('StartEvent_1');
// when
contextPad.open(element);
// then
expect(deleteAction(element)).to.exist;
}));
it('should NOT include delete action when rule returns false', inject(function(elementRegistry, contextPad, customRules) {
// given
customRules.addRule('elements.delete', function() {
return false;
});
var element = elementRegistry.get('StartEvent_1');
// when
contextPad.open(element);
// then
expect(deleteAction(element)).to.not.exist;
}));
it('should call rules with [ element ]', inject(function(elementRegistry, contextPad, customRules) {
// given
var element = elementRegistry.get('StartEvent_1');
customRules.addRule('elements.delete', function(context) {
// element array is actually passed
expect(context.elements).to.eql([ element ]);
return true;
});
// then
expect(function() {
contextPad.open(element);
}).not.to.throw;
}));
it('should include delete action when [ element ] is returned from rule', inject(function(elementRegistry, contextPad, customRules) {
// given
customRules.addRule('elements.delete', function(context) {
return context.elements;
});
var element = elementRegistry.get('StartEvent_1');
// when
contextPad.open(element);
// then
expect(deleteAction(element)).to.exist;
}));
it('should NOT include delete action when [ ] is returned from rule', inject(function(elementRegistry, contextPad, customRules) {
// given
customRules.addRule('elements.delete', function() {
return [];
});
var element = elementRegistry.get('StartEvent_1');
// when
contextPad.open(element);
// then
expect(deleteAction(element)).to.not.exist;
}));
});
describe('should show replace popup menu in the correct position ', function() {
var container;
@ -66,5 +182,7 @@ describe('features - context-pad', function() {
expect(replaceMenuRect.left).to.be.at.most(padMenuRect.left);
expect(replaceMenuRect.top).to.be.at.most(padMenuRect.bottom + padding);
}));
});
});

View File

@ -0,0 +1,15 @@
'use strict';
var inherits = require('inherits');
var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
function CustomRules(eventBus) {
RuleProvider.call(this, eventBus);
}
CustomRules.$inject = [ 'eventBus' ];
inherits(CustomRules, RuleProvider);
module.exports = CustomRules;

View File

@ -0,0 +1,4 @@
module.exports = {
__init__: [ 'customRules' ],
customRules: [ 'type', require('./CustomRules') ]
};