feat(replace): update bpmn when reconnecting flows
When reconnecting the source and end waypoints of a conditional or default flow, the bpmn tree should be updated to reflect these changes. Closes #373
This commit is contained in:
parent
188487ebdc
commit
534061d821
|
@ -127,7 +127,7 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
|
|||
|
||||
// attach / detach connection
|
||||
function updateConnection(e) {
|
||||
self.updateConnection(e.context.connection);
|
||||
self.updateConnection(e.context);
|
||||
}
|
||||
|
||||
this.executed([
|
||||
|
@ -168,6 +168,71 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
|
|||
'connection.reconnectStart'
|
||||
], ifBpmn(updateConnectionWaypoints));
|
||||
|
||||
|
||||
// update Default & Conditional flows
|
||||
this.executed([
|
||||
'connection.reconnectEnd',
|
||||
'connection.reconnectStart'
|
||||
], ifBpmn(function(e) {
|
||||
var context = e.context,
|
||||
connection = context.connection,
|
||||
businessObject = getBusinessObject(connection),
|
||||
oldSource = getBusinessObject(context.oldSource),
|
||||
oldTarget = getBusinessObject(context.oldTarget),
|
||||
newSource = getBusinessObject(connection.source),
|
||||
newTarget = getBusinessObject(connection.target);
|
||||
|
||||
if (oldSource === newSource || oldTarget === newTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// on reconnectStart -> default flow
|
||||
if (oldSource && oldSource.default) {
|
||||
context.default = oldSource.default;
|
||||
oldSource.default = undefined;
|
||||
}
|
||||
|
||||
// on reconnectEnd -> default flow
|
||||
if ((businessObject.sourceRef && businessObject.sourceRef.default) && !is(newTarget, 'bpmn:Activity')) {
|
||||
context.default = businessObject.sourceRef.default;
|
||||
businessObject.sourceRef.default = undefined;
|
||||
}
|
||||
|
||||
// on reconnectStart -> condtional flow
|
||||
if ((businessObject.conditionExpression) && is(oldSource, 'bpmn:Activity')) {
|
||||
context.conditionExpression = businessObject.conditionExpression;
|
||||
businessObject.conditionExpression = undefined;
|
||||
}
|
||||
|
||||
// on reconnectEnd -> condtional flow
|
||||
if ((businessObject.conditionExpression) && !is(newTarget, 'bpmn:Activity')) {
|
||||
context.conditionExpression = businessObject.conditionExpression;
|
||||
businessObject.conditionExpression = undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
this.reverted([
|
||||
'connection.reconnectEnd',
|
||||
'connection.reconnectStart'
|
||||
], ifBpmn(function(e) {
|
||||
var context = e.context,
|
||||
connection = context.connection,
|
||||
businessObject = getBusinessObject(connection),
|
||||
newSource = getBusinessObject(connection.source);
|
||||
|
||||
// default flow
|
||||
if (context.default) {
|
||||
if (is(newSource, 'bpmn:ExclusiveGateway') || is(newSource, 'bpmn:InclusiveGateway')) {
|
||||
newSource.default = context.default;
|
||||
}
|
||||
}
|
||||
|
||||
// conditional flow
|
||||
if (context.conditionExpression && is(newSource, 'bpmn:Activity')) {
|
||||
businessObject.conditionExpression = context.conditionExpression;
|
||||
}
|
||||
}));
|
||||
|
||||
// update attachments
|
||||
function updateAttachment(e) {
|
||||
self.updateAttachment(e.context);
|
||||
|
@ -428,9 +493,10 @@ BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
|
|||
};
|
||||
|
||||
|
||||
BpmnUpdater.prototype.updateConnection = function(connection) {
|
||||
BpmnUpdater.prototype.updateConnection = function(context) {
|
||||
|
||||
var businessObject = getBusinessObject(connection),
|
||||
var connection = context.connection,
|
||||
businessObject = getBusinessObject(connection),
|
||||
newSource = getBusinessObject(connection.source),
|
||||
newTarget = getBusinessObject(connection.target);
|
||||
|
||||
|
@ -492,4 +558,4 @@ function ifBpmn(fn) {
|
|||
fn(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,13 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
|
|||
businessObject.loopCharacteristics = oldBusinessObject.loopCharacteristics;
|
||||
}
|
||||
|
||||
// retain default flow's reference between inclusive and exclusive gateways
|
||||
if ((is(oldBusinessObject, 'bpmn:ExclusiveGateway') || is(oldBusinessObject, 'bpmn:InclusiveGateway')) &&
|
||||
(is(businessObject, 'bpmn:ExclusiveGateway') || is(businessObject, 'bpmn:InclusiveGateway')))
|
||||
{
|
||||
businessObject.default = oldBusinessObject.default;
|
||||
}
|
||||
|
||||
newElement = replace.replaceElement(element, newElement);
|
||||
|
||||
if (hints.select !== false) {
|
||||
|
@ -375,7 +382,7 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modelin
|
|||
// Add entries to replace menu
|
||||
forEach(filteredEntries, function(definition) {
|
||||
var entry = addMenuEntry(definition);
|
||||
|
||||
|
||||
menuEntries.push(entry);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ var modelingModule = require('../../../../lib/features/modeling'),
|
|||
moveModule = require('diagram-js/lib/features/move'),
|
||||
coreModule = require('../../../../lib/core');
|
||||
|
||||
var canvasEvent = require('../../../util/MockEvents').createCanvasEvent;
|
||||
|
||||
var is = require('../../../../lib/util/ModelUtil').is,
|
||||
isExpanded = require('../../../../lib/util/DiUtil').isExpanded,
|
||||
isInterrupting = require('../../../../lib/util/DiUtil').isInterrupting,
|
||||
|
@ -975,7 +973,8 @@ describe('features/replace', function() {
|
|||
}));
|
||||
|
||||
|
||||
it('should replace SequenceFlow with DefaultFlow -> undo', inject(function(elementRegistry, bpmnReplace, commandStack) {
|
||||
it('should replace SequenceFlow with DefaultFlow -> undo',
|
||||
inject(function(elementRegistry, bpmnReplace, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
|
||||
|
||||
|
@ -1013,6 +1012,152 @@ describe('features/replace', function() {
|
|||
expect(gateway.businessObject.default).to.equal(sequenceFlow.businessObject);
|
||||
}));
|
||||
|
||||
|
||||
it('should replace DefaultFlow with SequenceFlow when changing source',
|
||||
inject(function(elementRegistry, bpmnReplace, modeling) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
|
||||
task = elementRegistry.get('Task_2');
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectStart(sequenceFlow, task, [
|
||||
{ x: 686, y: 267, original: { x: 686, y: 307 } },
|
||||
{ x: 686, y: 207, original: { x: 686, y: 187 } }
|
||||
]);
|
||||
|
||||
var gateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// then
|
||||
expect(gateway.businessObject.default).to.not.exist;
|
||||
}));
|
||||
|
||||
|
||||
it('should replace DefaultFlow with SequenceFlow when changing source -> undo',
|
||||
inject(function(elementRegistry, bpmnReplace, modeling, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
|
||||
task = elementRegistry.get('Task_2');
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectStart(sequenceFlow, task, [
|
||||
{ x: 686, y: 267, original: { x: 686, y: 307 } },
|
||||
{ x: 686, y: 207, original: { x: 686, y: 187 } }
|
||||
]);
|
||||
|
||||
commandStack.undo();
|
||||
|
||||
var gateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// then
|
||||
expect(gateway.businessObject.default).equal(sequenceFlow.businessObject);
|
||||
}));
|
||||
|
||||
|
||||
it('should replace DefaultFlow with SequenceFlow when changing target',
|
||||
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
|
||||
root = canvas.getRootElement();
|
||||
|
||||
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
|
||||
|
||||
modeling.createShape(intermediateEvent, { x: 686, y: 50 }, root);
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
|
||||
{ x: 686, y: 267, original: { x: 686, y: 307 } },
|
||||
{ x: 686, y: 50, original: { x: 686, y: 75 } }
|
||||
]);
|
||||
|
||||
var gateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// then
|
||||
expect(gateway.businessObject.default).to.not.exist;
|
||||
}));
|
||||
|
||||
|
||||
it('should replace DefaultFlow with SequenceFlow when changing target -> undo',
|
||||
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
|
||||
root = canvas.getRootElement();
|
||||
|
||||
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
|
||||
|
||||
modeling.createShape(intermediateEvent, { x: 686, y: 50 }, root);
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
|
||||
{ x: 686, y: 267, original: { x: 686, y: 307 } },
|
||||
{ x: 686, y: 50, original: { x: 686, y: 75 } }
|
||||
]);
|
||||
|
||||
commandStack.undo();
|
||||
|
||||
var gateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// then
|
||||
expect(gateway.businessObject.default).equal(sequenceFlow.businessObject);
|
||||
}));
|
||||
|
||||
|
||||
it('should keep DefaultFlow when morphing Gateway', inject(function(elementRegistry, bpmnReplace) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
exclusiveGateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// when
|
||||
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
opts[0].action();
|
||||
|
||||
var inclusiveGateway = bpmnReplace.replaceElement(exclusiveGateway, { type: 'bpmn:InclusiveGateway'});
|
||||
|
||||
// then
|
||||
expect(inclusiveGateway.businessObject.default).to.equal(sequenceFlow.businessObject);
|
||||
}));
|
||||
|
||||
|
||||
it('should keep DefaultFlow when morphing Gateway -> undo',
|
||||
inject(function(elementRegistry, bpmnReplace, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
exclusiveGateway = elementRegistry.get('ExclusiveGateway_1');
|
||||
|
||||
// when
|
||||
var opts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger DefaultFlow replacement
|
||||
opts[0].action();
|
||||
|
||||
bpmnReplace.replaceElement(exclusiveGateway, { type: 'bpmn:InclusiveGateway'});
|
||||
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(exclusiveGateway.businessObject.default).to.equal(sequenceFlow.businessObject);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
@ -1098,6 +1243,104 @@ describe('features/replace', function() {
|
|||
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
|
||||
}));
|
||||
|
||||
|
||||
it('should replace ConditionalFlow with SequenceFlow when changing source',
|
||||
inject(function(elementRegistry, bpmnReplace, modeling) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
startEvent = elementRegistry.get('StartEvent_1');
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger ConditionalFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectStart(sequenceFlow, startEvent, [
|
||||
{ x: 196, y: 197, original: { x: 178, y: 197 } },
|
||||
{ x: 497, y: 278, original: { x: 547, y: 278 } }
|
||||
]);
|
||||
|
||||
// then
|
||||
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
|
||||
}));
|
||||
|
||||
|
||||
it('should replace ConditionalFlow with SequenceFlow when changing source -> undo',
|
||||
inject(function(elementRegistry, bpmnReplace, modeling, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
startEvent = elementRegistry.get('StartEvent_1');
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger ConditionalFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectStart(sequenceFlow, startEvent, [
|
||||
{ x: 196, y: 197, original: { x: 178, y: 197 } },
|
||||
{ x: 497, y: 278, original: { x: 547, y: 278 } }
|
||||
]);
|
||||
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(sequenceFlow.businessObject.conditionExpression.$type).to.equal('bpmn:FormalExpression');
|
||||
}));
|
||||
|
||||
|
||||
it('should replace ConditionalFlow with SequenceFlow when changing target',
|
||||
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
root = canvas.getRootElement(),
|
||||
intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
|
||||
|
||||
modeling.createShape(intermediateEvent, { x: 497, y: 197 }, root);
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger ConditionalFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
|
||||
{ x: 389, y: 197, original: { x: 389, y: 197 } },
|
||||
{ x: 497, y: 197, original: { x: 497, y: 197 } }
|
||||
]);
|
||||
|
||||
// then
|
||||
expect(sequenceFlow.businessObject.conditionExpression).to.not.exist;
|
||||
}));
|
||||
|
||||
|
||||
it('should replace ConditionalFlow with SequenceFlow when changing target -> undo',
|
||||
inject(function(elementRegistry, elementFactory, canvas, bpmnReplace, modeling, commandStack) {
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_3'),
|
||||
root = canvas.getRootElement(),
|
||||
intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent'});
|
||||
|
||||
modeling.createShape(intermediateEvent, { x: 497, y: 197 }, root);
|
||||
|
||||
var sequenceFlowOpts = bpmnReplace.getReplaceOptions(sequenceFlow);
|
||||
|
||||
// trigger ConditionalFlow replacement
|
||||
sequenceFlowOpts[0].action();
|
||||
|
||||
// when
|
||||
modeling.reconnectEnd(sequenceFlow, intermediateEvent, [
|
||||
{ x: 389, y: 197, original: { x: 389, y: 197 } },
|
||||
{ x: 497, y: 197, original: { x: 497, y: 197 } }
|
||||
]);
|
||||
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(sequenceFlow.businessObject.conditionExpression.$type).to.equal('bpmn:FormalExpression');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue