feat(DefaultFlows): allow default flows going out of tasks

Closes #425
This commit is contained in:
Ricardo Matias 2016-01-07 09:03:20 +01:00
parent 54881308c8
commit 0feebe0b0c
6 changed files with 161 additions and 11 deletions

View File

@ -1069,7 +1069,8 @@ function BpmnRenderer(eventBus, styles, pathMap, priority) {
}
// default marker
if (source.default && source.$instanceOf('bpmn:Gateway') && source.default === sequenceFlow) {
if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) &&
source.default === sequenceFlow) {
path.attr({
markerStart: marker('conditional-default-flow-marker')
});

View File

@ -222,7 +222,8 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
// default flow
if (context.default) {
if (is(newSource, 'bpmn:ExclusiveGateway') || is(newSource, 'bpmn:InclusiveGateway')) {
if (is(newSource, 'bpmn:ExclusiveGateway') || is(newSource, 'bpmn:InclusiveGateway') ||
is(newSource, 'bpmn:Activity')) {
newSource.default = context.default;
}
}

View File

@ -270,7 +270,8 @@ ReplaceMenuProvider.prototype._createSequenceFlowEntries = function (element, re
case 'replace-with-default-flow':
if (businessObject.sourceRef.default !== businessObject &&
(is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway'))) {
is(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:Activity'))) {
menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element.source, { default: businessObject });

View File

@ -76,9 +76,11 @@ function BpmnReplace(bpmnFactory, replace, selection, modeling) {
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')))
// retain default flow's reference between inclusive <-> exclusive gateways and activities
if ((is(oldBusinessObject, 'bpmn:ExclusiveGateway') || is(oldBusinessObject, 'bpmn:InclusiveGateway') ||
is(oldBusinessObject, 'bpmn:Activity')) &&
(is(businessObject, 'bpmn:ExclusiveGateway') || is(businessObject, 'bpmn:InclusiveGateway') ||
is(businessObject, 'bpmn:Activity')))
{
businessObject.default = oldBusinessObject.default;
}

View File

@ -25,13 +25,25 @@
<bpmn:incoming>SequenceFlow_4</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_4" sourceRef="StartEvent_1" targetRef="Task_4" />
<bpmn:task id="Task_1ei94kl" name="what to say?">
<bpmn:outgoing>SequenceFlow_15f5knn</bpmn:outgoing>
<bpmn:outgoing>SequenceFlow_10yqnek</bpmn:outgoing>
</bpmn:task>
<bpmn:task id="Task_0t0m2c3" name="hello">
<bpmn:incoming>SequenceFlow_15f5knn</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_15f5knn" sourceRef="Task_1ei94kl" targetRef="Task_0t0m2c3" />
<bpmn:task id="Task_15ymz00" name="bye">
<bpmn:incoming>SequenceFlow_10yqnek</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_10yqnek" sourceRef="Task_1ei94kl" targetRef="Task_15ymz00" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="ExclusiveGateway_1_di" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
<dc:Bounds x="452" y="282" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="332" y="337" width="90" height="20" />
<dc:Bounds x="347" y="305" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
@ -84,6 +96,31 @@
<dc:Bounds x="927.5" y="297" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Task_1ei94kl_di" bpmnElement="Task_1ei94kl">
<dc:Bounds x="1261" y="267" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_0t0m2c3_di" bpmnElement="Task_0t0m2c3">
<dc:Bounds x="1474" y="149" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_15f5knn_di" bpmnElement="SequenceFlow_15f5knn">
<di:waypoint xsi:type="dc:Point" x="1311" y="267" />
<di:waypoint xsi:type="dc:Point" x="1311" y="189" />
<di:waypoint xsi:type="dc:Point" x="1474" y="189" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1371" y="236" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Task_15ymz00_di" bpmnElement="Task_15ymz00">
<dc:Bounds x="1474" y="383" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_10yqnek_di" bpmnElement="SequenceFlow_10yqnek">
<di:waypoint xsi:type="dc:Point" x="1311" y="347" />
<di:waypoint xsi:type="dc:Point" x="1311" y="423" />
<di:waypoint xsi:type="dc:Point" x="1474" y="423" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1371" y="350" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -14,7 +14,8 @@ var coreModule = require('../../../../lib/core'),
customRulesModule = require('../../../util/custom-rules');
var domQuery = require('min-dom/lib/query'),
domClasses = require('min-dom/lib/classes');
domClasses = require('min-dom/lib/classes'),
find = require('lodash/collection/find');
var is = require('../../../../lib/util/ModelUtil').is,
isExpanded = require('../../../../lib/util/DiUtil').isExpanded;
@ -819,7 +820,7 @@ describe('features/replace-menu', function() {
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should show default replace option', inject(function(elementRegistry, popupMenu) {
it('should show default replace option [gateway]', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
@ -833,6 +834,20 @@ describe('features/replace-menu', function() {
}));
it('should show Default replace option [task]', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_15f5knn');
// when
openPopup(sequenceFlow);
var entriesContainer = queryPopup(popupMenu, '.djs-popup-body');
// then
expect(entriesContainer.childNodes).to.have.length(2);
}));
it('should NOT show default replace option', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_4');
@ -939,7 +954,7 @@ describe('features/replace-menu', function() {
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should replace SequenceFlow with DefaultFlow', inject(function(elementRegistry, popupMenu) {
it('should replace SequenceFlow with DefaultFlow [gateway]', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
@ -958,7 +973,28 @@ describe('features/replace-menu', function() {
}));
it('should replace SequenceFlow with DefaultFlow -> undo',
it('should replace SequenceFlow with DefaultFlow [task]', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_15f5knn');
// when
openPopup(sequenceFlow);
var entries = getEntries(popupMenu);
// trigger DefaultFlow replacement
var replaceDefaultFlow = find(entries, { id: 'replace-with-default-flow' });
replaceDefaultFlow.action();
var task = elementRegistry.get('Task_1ei94kl');
// then
expect(task.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should replace SequenceFlow with DefaultFlow [gateway] -> undo',
inject(function(elementRegistry, popupMenu, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_3');
@ -980,6 +1016,30 @@ describe('features/replace-menu', function() {
}));
it('should replace SequenceFlow with DefaultFlow [task] -> undo',
inject(function(elementRegistry, popupMenu, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_15f5knn');
// when
openPopup(sequenceFlow);
var entries = getEntries(popupMenu);
// trigger DefaultFlow replacement
var replaceDefaultFlow = find(entries, { id: 'replace-with-default-flow' });
replaceDefaultFlow.action();
commandStack.undo();
var task = elementRegistry.get('Task_1ei94kl');
// then
expect(task.businessObject.default).to.not.exist;
}));
it('should only have one DefaultFlow', inject(function(elementRegistry, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_1'),
@ -1145,6 +1205,28 @@ describe('features/replace-menu', function() {
}));
it('should keep DefaultFlow when morphing Task', inject(function(elementRegistry, bpmnReplace, popupMenu) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_15f5knn'),
task = elementRegistry.get('Task_1ei94kl');
// when
openPopup(sequenceFlow);
var entries = getEntries(popupMenu);
// trigger DefaultFlow replacement
var replaceDefaultFlow = find(entries, { id: 'replace-with-default-flow' });
replaceDefaultFlow.action();
var sendTask = bpmnReplace.replaceElement(task, { type: 'bpmn:SendTask'});
// then
expect(sendTask.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should keep DefaultFlow when morphing Gateway -> undo',
inject(function(elementRegistry, bpmnReplace, popupMenu, commandStack) {
// given
@ -1166,6 +1248,32 @@ describe('features/replace-menu', function() {
// then
expect(exclusiveGateway.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
it('should keep DefaultFlow when morphing Task -> undo',
inject(function(elementRegistry, bpmnReplace, popupMenu, commandStack) {
// given
var sequenceFlow = elementRegistry.get('SequenceFlow_15f5knn'),
task = elementRegistry.get('Task_1ei94kl');
// when
openPopup(sequenceFlow);
var entries = getEntries(popupMenu);
// trigger DefaultFlow replacement
var replaceDefaultFlow = find(entries, { id: 'replace-with-default-flow' });
replaceDefaultFlow.action();
bpmnReplace.replaceElement(task, { type: 'bpmn:SendTask'});
commandStack.undo();
// then
expect(task.businessObject.default).to.equal(sequenceFlow.businessObject);
}));
});