feat: reconnect message flows when collapsing participant

Related to camunda/camunda-modeler#1651
This commit is contained in:
Philipp Fromme 2021-04-21 11:19:33 +02:00 committed by fake-join[bot]
parent 410beadae1
commit 4806507936
5 changed files with 331 additions and 3 deletions

View File

@ -0,0 +1,89 @@
import inherits from 'inherits';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { is } from '../../../util/ModelUtil';
import { isExpanded } from '../../../util/DiUtil';
import { selfAndAllChildren } from 'diagram-js/lib/util/Elements';
import {
getResizedSourceAnchor,
getResizedTargetAnchor
} from 'diagram-js/lib/features/modeling/cmd/helper/AnchorsHelper';
/**
* BPMN-specific message flow behavior.
*/
export default function MessageFlowBehavior(eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
this.postExecute('shape.replace', function(context) {
var oldShape = context.oldShape,
newShape = context.newShape;
if (!isParticipantCollapse(oldShape, newShape)) {
return;
}
var messageFlows = getMessageFlows(oldShape);
messageFlows.incoming.forEach(function(incoming) {
var anchor = getResizedTargetAnchor(incoming, newShape, oldShape);
modeling.reconnectEnd(incoming, newShape, anchor);
});
messageFlows.outgoing.forEach(function(outgoing) {
var anchor = getResizedSourceAnchor(outgoing, newShape, oldShape);
modeling.reconnectStart(outgoing, newShape, anchor);
});
}, true);
}
MessageFlowBehavior.$inject = [ 'eventBus', 'modeling' ];
inherits(MessageFlowBehavior, CommandInterceptor);
// helpers //////////
function isParticipantCollapse(oldShape, newShape) {
return is(oldShape, 'bpmn:Participant')
&& isExpanded(oldShape)
&& is(newShape, 'bpmn:Participant')
&& !isExpanded(newShape);
}
function getMessageFlows(parent) {
var elements = selfAndAllChildren([ parent ], false);
var incoming = [],
outgoing = [];
elements.forEach(function(element) {
if (element === parent) {
return;
}
element.incoming.forEach(function(connection) {
if (is(connection, 'bpmn:MessageFlow')) {
incoming.push(connection);
}
});
element.outgoing.forEach(function(connection) {
if (is(connection, 'bpmn:MessageFlow')) {
outgoing.push(connection);
}
});
}, []);
return {
incoming: incoming,
outgoing: outgoing
};
}

View File

@ -18,6 +18,7 @@ import GroupBehavior from './GroupBehavior';
import ImportDockingFix from './ImportDockingFix';
import IsHorizontalFix from './IsHorizontalFix';
import LabelBehavior from './LabelBehavior';
import MessageFlowBehavior from './MessageFlowBehavior';
import ModelingFeedback from './ModelingFeedback';
import ReplaceConnectionBehavior from './ReplaceConnectionBehavior';
import RemoveParticipantBehavior from './RemoveParticipantBehavior';
@ -54,6 +55,7 @@ export default {
'importDockingFix',
'isHorizontalFix',
'labelBehavior',
'messageFlowBehavior',
'modelingFeedback',
'removeElementBehavior',
'removeParticipantBehavior',
@ -88,6 +90,7 @@ export default {
importDockingFix: [ 'type', ImportDockingFix ],
isHorizontalFix: [ 'type', IsHorizontalFix ],
labelBehavior: [ 'type', LabelBehavior ],
messageFlowBehavior: [ 'type', MessageFlowBehavior ],
modelingFeedback: [ 'type', ModelingFeedback ],
replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ],
removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ],

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" id="simple" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.7.0" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:collaboration id="Collaboration_1">
<bpmn2:participant id="Participant_1" name="Participant_1" processRef="Process_1" />
<bpmn2:participant id="Participant_2" name="Participant_2" processRef="Process_2" />
<bpmn2:participant id="Participant_3" name="Participant_3" processRef="Process_3" />
<bpmn2:participant id="Participant_4" name="Participant_4" processRef="Process_4" />
<bpmn2:messageFlow id="Flow_2" name="Flow_2" sourceRef="Activity_3" targetRef="Activity_1" />
<bpmn2:messageFlow id="Flow_4" name="Flow_4" sourceRef="Activity_4" targetRef="Activity_3" />
<bpmn2:messageFlow id="Flow_3" name="Flow_3" sourceRef="Activity_4" targetRef="Activity_2" />
<bpmn2:messageFlow id="Flow_1" name="Flow_1" sourceRef="Activity_2" targetRef="Activity_1" />
</bpmn2:collaboration>
<bpmn2:process id="Process_1" isExecutable="false">
<bpmn2:task id="Activity_1" name="Activity_1" />
</bpmn2:process>
<bpmn2:process id="Process_2" isExecutable="false">
<bpmn2:task id="Activity_2" name="Activity_2" />
</bpmn2:process>
<bpmn2:process id="Process_3" isExecutable="false">
<bpmn2:task id="Activity_3" name="Activity_3" />
</bpmn2:process>
<bpmn2:process id="Process_4" isExecutable="false">
<bpmn2:task id="Activity_4" name="Activity_4" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="Participant_1xi2u59_di" bpmnElement="Participant_1" isHorizontal="true">
<dc:Bounds x="180" y="80" width="330" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1ajk3x2_di" bpmnElement="Activity_1">
<dc:Bounds x="310" y="160" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_1bs1k5p_di" bpmnElement="Participant_2" isHorizontal="true">
<dc:Bounds x="180" y="400" width="330" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_19rbcko_di" bpmnElement="Activity_2">
<dc:Bounds x="310" y="480" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_1gev1ss_di" bpmnElement="Participant_3" isHorizontal="true">
<dc:Bounds x="610" y="400" width="330" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0tq9cy6_di" bpmnElement="Activity_3">
<dc:Bounds x="740" y="480" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_1r1lj0k_di" bpmnElement="Participant_4" isHorizontal="true">
<dc:Bounds x="610" y="720" width="330" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_18zqri6_di" bpmnElement="Activity_4">
<dc:Bounds x="740" y="800" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_10f8p6o_di" bpmnElement="Flow_2" bioc:stroke="#000" bioc:fill="#fff">
<di:waypoint x="790" y="480" />
<di:waypoint x="790" y="360" />
<di:waypoint x="370" y="360" />
<di:waypoint x="370" y="240" />
<bpmndi:BPMNLabel>
<dc:Bounds x="562" y="342" width="36" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1hgc2dc_di" bpmnElement="Flow_4" bioc:stroke="#000" bioc:fill="#fff">
<di:waypoint x="800" y="800" />
<di:waypoint x="800" y="560" />
<bpmndi:BPMNLabel>
<dc:Bounds x="797" y="677" width="37" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0hj61b5_di" bpmnElement="Flow_3" bioc:stroke="#000" bioc:fill="#fff">
<di:waypoint x="780" y="800" />
<di:waypoint x="780" y="680" />
<di:waypoint x="360" y="680" />
<di:waypoint x="360" y="560" />
<bpmndi:BPMNLabel>
<dc:Bounds x="552" y="662" width="36" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1oa1qkv_di" bpmnElement="Flow_1">
<di:waypoint x="350" y="480" />
<di:waypoint x="350" y="240" />
<bpmndi:BPMNLabel>
<dc:Bounds x="347" y="357" width="36" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,151 @@
import {
bootstrapModeler,
inject
} from 'test/TestHelper';
import coreModule from 'lib/core';
import modelingModule from 'lib/features/modeling';
describe('features/modeling - message flow behavior', function() {
var testModules = [ coreModule, modelingModule ];
describe('when collapsing participant', function() {
var processDiagramXML = require('./MessageFlowBehavior.bpmn');
beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
it('should reconnect message flows to collapsed participant (incoming)', inject(
function(bpmnReplace, elementRegistry) {
// given
var participant = elementRegistry.get('Participant_1');
// when
participant = bpmnReplace.replaceElement(participant, {
type: 'bpmn:Participant',
isExpanded: false
});
// then
expect(participant.incoming).to.have.length(2);
expect(elementRegistry.get('Flow_1').waypoints).to.eql([
{
original: {
x: 350,
y: 520
},
x: 350,
y: 480
},
{
original: {
x: 350,
y: 110
},
x: 350,
y: 140
}
]);
expect(elementRegistry.get('Flow_2').waypoints).to.eql([
{
original: {
x: 790,
y: 520
},
x: 790,
y: 480
},
{
x: 790,
y: 360
},
{
x: 370,
y: 360
},
{
original: {
x: 370,
y: 110
},
x: 370,
y: 140
}
]);
}
));
it('should reconnect message flows to collapsed participant (outgoing)', inject(
function(bpmnReplace, elementRegistry) {
// given
var participant = elementRegistry.get('Participant_4');
// when
participant = bpmnReplace.replaceElement(participant, {
type: 'bpmn:Participant',
isExpanded: false
});
// then
expect(participant.outgoing).to.have.length(2);
expect(elementRegistry.get('Flow_3').waypoints).to.eql([
{
original: {
x: 780,
y: 750
},
x: 780,
y: 720
},
{
x: 780,
y: 680
},
{
x: 360,
y: 680
},
{
original: {
x: 360,
y: 520
},
x: 360,
y: 560
}
]);
expect(elementRegistry.get('Flow_4').waypoints).to.eql([
{
original: {
x: 800,
y: 750
},
x: 800,
y: 720
},
{
original: {
x: 800,
y: 520
},
x: 800,
y: 560
}
]);
}
));
});
});

View File

@ -355,7 +355,7 @@ describe('features/replace - bpmn replace', function() {
});
describe('should collapse pool, removing message flows', function() {
describe('should collapse pool, reconnecting message flows', function() {
var diagramXML = require('./BpmnReplace.poolMessageFlows.bpmn');
@ -382,8 +382,8 @@ describe('features/replace - bpmn replace', function() {
expect(isExpanded(newShape)).to.be.false; // collapsed
expect(newShape.children).to.be.empty;
expect(elementRegistry.get('MessageFlow_1')).not.to.exist;
expect(elementRegistry.get('MessageFlow_2')).not.to.exist;
expect(elementRegistry.get('MessageFlow_1')).to.exist;
expect(elementRegistry.get('MessageFlow_2')).to.exist;
}));
});