feat(replace): properly collapse / expand SubProcess(es)

* correctly toggle collapse / expand state
* update children visibility

Closes #575
Closes #510

review(collapse-expand)
This commit is contained in:
hoferch91 2016-06-16 13:57:32 +02:00 committed by Nico Rehwaldt
parent 4714a7279d
commit 226a0d76ed
11 changed files with 1235 additions and 222 deletions

View File

@ -0,0 +1,139 @@
'use strict';
var inherits = require('inherits');
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor'),
getBusinessObject = require('../../../util/ModelUtil').getBusinessObject,
is = require('../../../util/ModelUtil').is,
computeChildrenBBox = require('diagram-js/lib/features/resize/ResizeUtil').computeChildrenBBox;
var LOW_PRIORITY = 500;
function ToggleElementCollapseBehaviour(eventBus, elementFactory, modeling, resize) {
CommandInterceptor.call(this, eventBus);
function hideEmptyLables(children) {
if (children.length) {
children.forEach(function(child) {
if (child.type === 'label' && !child.businessObject.name) {
child.hidden = true;
}
});
}
}
function expandedBounds(shape, defaultSize) {
var children = shape.children,
newBounds = defaultSize,
visibleElements,
visibleBBox;
visibleElements = filterVisible(children).concat([ shape ]);
visibleBBox = computeChildrenBBox(visibleElements);
if (visibleBBox) {
// center to visibleBBox with max(defaultSize, childrenBounds)
newBounds.width = Math.max(visibleBBox.width, newBounds.width);
newBounds.height = Math.max(visibleBBox.height, newBounds.height);
newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2;
newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2;
} else {
// center to collapsed shape with defaultSize
newBounds.x = shape.x + (shape.width - newBounds.width) / 2;
newBounds.y = shape.y + (shape.height - newBounds.height) / 2;
}
return newBounds;
}
function collapsedBounds(shape, defaultSize) {
return {
x: shape.x + (shape.width - defaultSize.width) / 2,
y: shape.y + (shape.height - defaultSize.height) / 2,
width: defaultSize.width,
height: defaultSize.height
};
}
this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var context = e.context,
shape = context.shape;
if (!is(shape, 'bpmn:SubProcess')) {
return;
}
if (!shape.collapsed) {
// all children got made visible through djs, hide empty labels
hideEmptyLables(shape.children);
// remove collapsed marker
getBusinessObject(shape).di.isExpanded = true;
} else {
// place collapsed marker
getBusinessObject(shape).di.isExpanded = false;
}
});
this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var context = e.context;
var shape = context.shape;
// revert removing/placing collapsed marker
if (!shape.collapsed) {
getBusinessObject(shape).di.isExpanded = true;
} else {
getBusinessObject(shape).di.isExpanded = false;
}
});
this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var shape = e.context.shape,
defaultSize = elementFactory._getDefaultSize(shape),
newBounds;
if (shape.collapsed) {
// resize to default size of collapsed shapes
newBounds = collapsedBounds(shape, defaultSize);
} else {
// resize to bounds of max(visible children, defaultSize)
newBounds = expandedBounds(shape, defaultSize);
}
modeling.resizeShape(shape, newBounds);
});
}
inherits(ToggleElementCollapseBehaviour, CommandInterceptor);
ToggleElementCollapseBehaviour.$inject = [
'eventBus',
'elementFactory',
'modeling'
];
module.exports = ToggleElementCollapseBehaviour;
/////// helpers ///////////////////////////
function filterVisible(elements) {
return elements.filter(function(e) {
return !e.hidden;
});
}

View File

@ -18,7 +18,8 @@ module.exports = {
'unsetDefaultFlowBehavior',
'updateFlowNodeRefsBehavior',
'removeElementBehavior',
'unclaimIdBehavior'
'unclaimIdBehavior',
'toggleElementCollapseBehaviour'
],
appendBehavior: [ 'type', require('./AppendBehavior') ],
copyPasteBehavior: [ 'type', require('./CopyPasteBehavior') ],
@ -38,5 +39,6 @@ module.exports = {
unsetDefaultFlowBehavior: [ 'type', require('./UnsetDefaultFlowBehavior') ],
updateFlowNodeRefsBehavior: [ 'type', require('./UpdateFlowNodeRefsBehavior') ],
removeElementBehavior: [ 'type', require('./RemoveElementBehavior') ],
unclaimIdBehavior: [ 'type', require('./UnclaimIdBehavior') ]
unclaimIdBehavior: [ 'type', require('./UnclaimIdBehavior') ],
toggleElementCollapseBehaviour : [ 'type', require('./ToggleElementCollapseBehaviour') ]
};

View File

@ -7,7 +7,8 @@ var is = require('../../util/ModelUtil').is,
isDifferentType = require('./util/TypeUtil').isDifferentType;
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter');
filter = require('lodash/collection/filter'),
reject = require('lodash/collection/reject');
var replaceOptions = require ('../replace/ReplaceOptions');
@ -210,6 +211,13 @@ ReplaceMenuProvider.prototype.getEntries = function(element) {
});
}
// collapsed SubProcess can not be replaced with itself
if (is(businessObject, 'bpmn:SubProcess') && !isExpanded(businessObject)) {
entries = reject(entries, function(entry) {
return entry.label === 'Sub Process (collapsed)';
});
}
return this._createEntries(element, entries);
}

View File

@ -1,7 +1,8 @@
'use strict';
var pick = require('lodash/object/pick'),
assign = require('lodash/object/assign');
assign = require('lodash/object/assign'),
has = require('lodash/object/has');
var is = require('../../util/ModelUtil').is,
isExpanded = require('../../util/DiUtil').isExpanded,
@ -15,6 +16,30 @@ var CUSTOM_PROPERTIES = [
'isInterrupting'
];
function toggeling(element, target) {
var oldCollapsed = has(element, 'collapsed') ?
element.collapsed : !isExpanded(element);
var targetCollapsed;
if (has(target, 'collapsed') || has(target, 'isExpanded')) {
// property is explicitly set so use it
targetCollapsed = has(target, 'collapsed') ?
target.collapsed : !target.isExpanded;
} else {
// keep old state
targetCollapsed = oldCollapsed;
}
if (oldCollapsed !== targetCollapsed) {
element.collapsed = oldCollapsed;
return true;
}
return false;
}
/**
* This module takes care of replacing BPMN elements
@ -36,8 +61,23 @@ function BpmnReplace(bpmnFactory, replace, selection, modeling) {
hints = hints || {};
var type = target.type,
oldBusinessObject = element.businessObject,
newBusinessObject = bpmnFactory.create(type);
oldBusinessObject = element.businessObject;
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
if (type === 'bpmn:SubProcess') {
if (toggeling(element, target)) {
// expanding or collapsing process
modeling.toggleCollapse(element);
return element;
}
}
}
var newBusinessObject = bpmnFactory.create(type);
var newElement = {
type: type,
@ -52,21 +92,16 @@ function BpmnReplace(bpmnFactory, replace, selection, modeling) {
// initialize special properties defined in target definition
assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
newElement.isExpanded = isExpanded(oldBusinessObject);
}
// preserve adhoc state while switching collapsed/expanded subprocess
if (is(oldBusinessObject, 'bpmn:AdHocSubProcess') && target.isExpanded) {
newElement.businessObject = bpmnFactory.create('bpmn:AdHocSubProcess');
}
if (is(oldBusinessObject, 'bpmn:Activity')) {
// switch collapsed/expanded subprocesses
if (target.isExpanded === true) {
newElement.isExpanded = true;
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
// no toggeling, so keep old state
newElement.isExpanded = isExpanded(oldBusinessObject);
}
// else if property is explicitly set, use it
else if (has(target, 'isExpanded')) {
newElement.isExpanded = target.isExpanded;
}
// TODO: need also to respect min/max Size

View File

@ -357,6 +357,15 @@ module.exports.SUBPROCESS_EXPANDED = [
triggeredByEvent: true,
isExpanded: true
}
},
{
label: 'Sub Process (collapsed)',
actionName: 'replace-with-collapsed-subprocess',
className: 'bpmn-icon-subprocess-collapsed',
target: {
type: 'bpmn:SubProcess',
isExpanded: false
}
}
];

View File

@ -1,149 +1,273 @@
<?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" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_U0Z10NLzEeSKyddZMwwjAA" exporter="camunda modeler" exporterVersion="2.7.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="Process_1" isExecutable="false">
<bpmn2:subProcess id="SubProcess_1">
<bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_4</bpmn2:outgoing>
<bpmn2:startEvent id="StartEvent_2">
<bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_2" name="" sourceRef="StartEvent_2" targetRef="ExclusiveGateway_1"/>
<bpmn2:exclusiveGateway id="ExclusiveGateway_1">
<bpmn2:incoming>SequenceFlow_2</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_5</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_6</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="SequenceFlow_5" name="" sourceRef="ExclusiveGateway_1" targetRef="Task_1"/>
<bpmn2:sequenceFlow id="SequenceFlow_6" name="" sourceRef="ExclusiveGateway_1" targetRef="ScriptTask_1"/>
<bpmn2:scriptTask id="ScriptTask_1">
<bpmn2:incoming>SequenceFlow_6</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_7</bpmn2:outgoing>
</bpmn2:scriptTask>
<bpmn2:sequenceFlow id="SequenceFlow_7" name="" sourceRef="ScriptTask_1" targetRef="EndEvent_3"/>
<bpmn2:task id="Task_1">
<bpmn2:incoming>SequenceFlow_5</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_3</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="SequenceFlow_3" name="" sourceRef="Task_1" targetRef="EndEvent_1"/>
<bpmn2:endEvent id="EndEvent_1">
<bpmn2:incoming>SequenceFlow_3</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:endEvent id="EndEvent_3">
<bpmn2:incoming>SequenceFlow_7</bpmn2:incoming>
</bpmn2:endEvent>
</bpmn2:subProcess>
<bpmn2:sequenceFlow id="SequenceFlow_4" name="" sourceRef="SubProcess_1" targetRef="EndEvent_2"/>
<bpmn2:startEvent id="StartEvent_1">
<bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1" name="" sourceRef="StartEvent_1" targetRef="SubProcess_1"/>
<bpmn2:endEvent id="EndEvent_2">
<bpmn2:incoming>SequenceFlow_4</bpmn2:incoming>
</bpmn2:endEvent>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_26" bpmnElement="StartEvent_1">
<dc:Bounds height="36.0" width="36.0" x="300.0" y="229.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="318.0" y="270.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_8" bpmnElement="SubProcess_1">
<dc:Bounds height="80.0" width="100.0" x="532.0" y="207.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_1" bpmnElement="SequenceFlow_1" sourceElement="_BPMNShape_StartEvent_26" targetElement="_BPMNShape_SubProcess_8">
<di:waypoint xsi:type="dc:Point" x="336.0" y="247.0"/>
<di:waypoint xsi:type="dc:Point" x="532.0" y="247.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="364.0" y="247.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_27" bpmnElement="StartEvent_2">
<dc:Bounds height="36.0" width="36.0" x="396.0" y="144.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="414.0" y="185.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task_1">
<dc:Bounds height="80.0" width="100.0" x="564.0" y="132.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_2" bpmnElement="SequenceFlow_2" sourceElement="_BPMNShape_StartEvent_27" targetElement="_BPMNShape_ExclusiveGateway_19">
<di:waypoint xsi:type="dc:Point" x="432.0" y="162.0"/>
<di:waypoint xsi:type="dc:Point" x="480.0" y="162.0"/>
<di:waypoint xsi:type="dc:Point" x="480.0" y="198.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="454.0" y="162.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_58" bpmnElement="EndEvent_1">
<dc:Bounds height="36.0" width="36.0" x="732.0" y="144.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="750.0" y="185.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_3" bpmnElement="SequenceFlow_3" sourceElement="_BPMNShape_Task_3" targetElement="_BPMNShape_EndEvent_58">
<di:waypoint xsi:type="dc:Point" x="664.0" y="172.0"/>
<di:waypoint xsi:type="dc:Point" x="704.0" y="172.0"/>
<di:waypoint xsi:type="dc:Point" x="704.0" y="162.0"/>
<di:waypoint xsi:type="dc:Point" x="732.0" y="162.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="686.0" y="172.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_59" bpmnElement="EndEvent_2">
<dc:Bounds height="36.0" width="36.0" x="840.0" y="229.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="858.0" y="270.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_4" bpmnElement="SequenceFlow_4" sourceElement="_BPMNShape_SubProcess_8" targetElement="_BPMNShape_EndEvent_59">
<di:waypoint xsi:type="dc:Point" x="632.0" y="247.0"/>
<di:waypoint xsi:type="dc:Point" x="813.0" y="246.0"/>
<di:waypoint xsi:type="dc:Point" x="840.0" y="247.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="634.0" y="247.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_ExclusiveGateway_19" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
<dc:Bounds height="50.0" width="50.0" x="455.0" y="198.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="480.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_5" bpmnElement="SequenceFlow_5" sourceElement="_BPMNShape_ExclusiveGateway_19" targetElement="_BPMNShape_Task_3">
<di:waypoint xsi:type="dc:Point" x="505.0" y="223.0"/>
<di:waypoint xsi:type="dc:Point" x="541.0" y="223.0"/>
<di:waypoint xsi:type="dc:Point" x="541.0" y="172.0"/>
<di:waypoint xsi:type="dc:Point" x="564.0" y="172.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="538.0" y="218.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_ScriptTask_2" bpmnElement="ScriptTask_1">
<dc:Bounds height="80.0" width="100.0" x="555.0" y="282.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_6" bpmnElement="SequenceFlow_6" sourceElement="_BPMNShape_ExclusiveGateway_19" targetElement="_BPMNShape_ScriptTask_2">
<di:waypoint xsi:type="dc:Point" x="480.0" y="248.0"/>
<di:waypoint xsi:type="dc:Point" x="480.0" y="322.0"/>
<di:waypoint xsi:type="dc:Point" x="555.0" y="322.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="478.0" y="322.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_60" bpmnElement="EndEvent_3">
<dc:Bounds height="36.0" width="36.0" x="705.0" y="304.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="723.0" y="345.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_7" bpmnElement="SequenceFlow_7" sourceElement="_BPMNShape_ScriptTask_2" targetElement="_BPMNShape_EndEvent_60">
<di:waypoint xsi:type="dc:Point" x="655.0" y="322.0"/>
<di:waypoint xsi:type="dc:Point" x="705.0" y="322.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="677.0" y="322.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
<?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" id="_U0Z10NLzEeSKyddZMwwjAA" targetNamespace="http://camunda.org/schema/1.0/bpmn" exporter="Camunda Modeler" exporterVersion="0.7.0-dev" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="Process_1" isExecutable="false">
<bpmn2:subProcess id="SubProcess_1">
<bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_4</bpmn2:outgoing>
<bpmn2:startEvent id="StartEvent_2" name="StartEvent_2">
<bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_2" name="" sourceRef="StartEvent_2" targetRef="ExclusiveGateway_1" />
<bpmn2:exclusiveGateway id="ExclusiveGateway_1">
<bpmn2:incoming>SequenceFlow_2</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_5</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_6</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="SequenceFlow_5" name="" sourceRef="ExclusiveGateway_1" targetRef="Task_1" />
<bpmn2:sequenceFlow id="SequenceFlow_6" name="" sourceRef="ExclusiveGateway_1" targetRef="ScriptTask_1" />
<bpmn2:scriptTask id="ScriptTask_1">
<bpmn2:incoming>SequenceFlow_6</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_7</bpmn2:outgoing>
</bpmn2:scriptTask>
<bpmn2:sequenceFlow id="SequenceFlow_7" name="" sourceRef="ScriptTask_1" targetRef="EndEvent_3" />
<bpmn2:task id="Task_1">
<bpmn2:incoming>SequenceFlow_5</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_3</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="SequenceFlow_3" name="" sourceRef="Task_1" targetRef="EndEvent_1" />
<bpmn2:endEvent id="EndEvent_1" name="EndEvent_1">
<bpmn2:incoming>SequenceFlow_3</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:endEvent id="EndEvent_3" name="EndEvent_3">
<bpmn2:incoming>SequenceFlow_7</bpmn2:incoming>
</bpmn2:endEvent>
</bpmn2:subProcess>
<bpmn2:sequenceFlow id="SequenceFlow_4" name="" sourceRef="SubProcess_1" targetRef="EndEvent_2" />
<bpmn2:startEvent id="StartEvent_1">
<bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1" name="" sourceRef="StartEvent_1" targetRef="SubProcess_1" />
<bpmn2:endEvent id="EndEvent_2">
<bpmn2:incoming>SequenceFlow_4</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:startEvent id="StartEvent_4">
<bpmn2:outgoing>SequenceFlow_9</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_9" sourceRef="StartEvent_4" targetRef="SubProcess_2" />
<bpmn2:endEvent id="EndEvent_5">
<bpmn2:incoming>SequenceFlow_8</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="SequenceFlow_8" sourceRef="SubProcess_2" targetRef="EndEvent_5" />
<bpmn2:subProcess id="SubProcess_3" />
<bpmn2:adHocSubProcess id="SubProcess_4">
<bpmn2:multiInstanceLoopCharacteristics />
<bpmn2:startEvent id="StartEvent_5" />
</bpmn2:adHocSubProcess>
<bpmn2:adHocSubProcess id="SubProcess_2">
<bpmn2:incoming>SequenceFlow_9</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_8</bpmn2:outgoing>
<bpmn2:multiInstanceLoopCharacteristics />
<bpmn2:startEvent id="StartEvent_3">
<bpmn2:outgoing>SequenceFlow_10</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:task id="Task_2">
<bpmn2:incoming>SequenceFlow_10</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_11</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:endEvent id="EndEvent_4">
<bpmn2:incoming>SequenceFlow_11</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="SequenceFlow_10" sourceRef="StartEvent_3" targetRef="Task_2" />
<bpmn2:sequenceFlow id="SequenceFlow_11" sourceRef="Task_2" targetRef="EndEvent_4" />
</bpmn2:adHocSubProcess>
<bpmn2:subProcess id="SubProcess_5">
<bpmn2:startEvent id="StartEvent_6" />
</bpmn2:subProcess>
<bpmn2:subProcess id="SubProcess_6" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_26" bpmnElement="StartEvent_1">
<dc:Bounds x="300" y="176" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="273" y="217" width="90" height="0" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_8" bpmnElement="SubProcess_1">
<dc:Bounds x="532" y="154" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_1" bpmnElement="SequenceFlow_1" sourceElement="_BPMNShape_StartEvent_26" targetElement="_BPMNShape_SubProcess_8">
<di:waypoint xsi:type="dc:Point" x="336" y="194" />
<di:waypoint xsi:type="dc:Point" x="532" y="194" />
<bpmndi:BPMNLabel>
<dc:Bounds x="364" y="194" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_27" bpmnElement="StartEvent_2">
<dc:Bounds x="396" y="91" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="370" y="132" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task_1">
<dc:Bounds x="564" y="79" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_2" bpmnElement="SequenceFlow_2" sourceElement="_BPMNShape_StartEvent_27" targetElement="_BPMNShape_ExclusiveGateway_19">
<di:waypoint xsi:type="dc:Point" x="432" y="109" />
<di:waypoint xsi:type="dc:Point" x="480" y="109" />
<di:waypoint xsi:type="dc:Point" x="480" y="145" />
<bpmndi:BPMNLabel>
<dc:Bounds x="412" y="109" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_58" bpmnElement="EndEvent_1">
<dc:Bounds x="767" y="91" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="740" y="132" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_3" bpmnElement="SequenceFlow_3" sourceElement="_BPMNShape_Task_3" targetElement="_BPMNShape_EndEvent_58">
<di:waypoint xsi:type="dc:Point" x="664" y="119" />
<di:waypoint xsi:type="dc:Point" x="704" y="119" />
<di:waypoint xsi:type="dc:Point" x="704" y="109" />
<di:waypoint xsi:type="dc:Point" x="767" y="109" />
<bpmndi:BPMNLabel>
<dc:Bounds x="674" y="111" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_59" bpmnElement="EndEvent_2">
<dc:Bounds x="875" y="176" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="848" y="217" width="90" height="0" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_4" bpmnElement="SequenceFlow_4" sourceElement="_BPMNShape_SubProcess_8" targetElement="_BPMNShape_EndEvent_59">
<di:waypoint xsi:type="dc:Point" x="632" y="194" />
<di:waypoint xsi:type="dc:Point" x="813" y="195" />
<di:waypoint xsi:type="dc:Point" x="875" y="195" />
<bpmndi:BPMNLabel>
<dc:Bounds x="677.5" y="176.5" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_ExclusiveGateway_19" bpmnElement="ExclusiveGateway_1" isMarkerVisible="true">
<dc:Bounds x="455" y="145" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="435" y="200" width="90" height="0" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_5" bpmnElement="SequenceFlow_5" sourceElement="_BPMNShape_ExclusiveGateway_19" targetElement="_BPMNShape_Task_3">
<di:waypoint xsi:type="dc:Point" x="505" y="170" />
<di:waypoint xsi:type="dc:Point" x="541" y="170" />
<di:waypoint xsi:type="dc:Point" x="541" y="119" />
<di:waypoint xsi:type="dc:Point" x="564" y="119" />
<bpmndi:BPMNLabel>
<dc:Bounds x="496" y="165" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_ScriptTask_2" bpmnElement="ScriptTask_1">
<dc:Bounds x="555" y="229" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_6" bpmnElement="SequenceFlow_6" sourceElement="_BPMNShape_ExclusiveGateway_19" targetElement="_BPMNShape_ScriptTask_2">
<di:waypoint xsi:type="dc:Point" x="480" y="195" />
<di:waypoint xsi:type="dc:Point" x="480" y="269" />
<di:waypoint xsi:type="dc:Point" x="555" y="269" />
<bpmndi:BPMNLabel>
<dc:Bounds x="391" y="662" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_60" bpmnElement="EndEvent_3">
<dc:Bounds x="740" y="272" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="668" y="685" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_7" bpmnElement="SequenceFlow_7" sourceElement="_BPMNShape_ScriptTask_2" targetElement="_BPMNShape_EndEvent_60">
<di:waypoint xsi:type="dc:Point" x="655" y="269" />
<di:waypoint xsi:type="dc:Point" x="680" y="269" />
<di:waypoint xsi:type="dc:Point" x="680" y="290" />
<di:waypoint xsi:type="dc:Point" x="740" y="290" />
<bpmndi:BPMNLabel>
<dc:Bounds x="650" y="276.5" width="90" height="6" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="StartEvent_14sl8b4_di" bpmnElement="StartEvent_4">
<dc:Bounds x="287" y="417" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="260" y="453" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1t8nq5v_di" bpmnElement="SequenceFlow_9">
<di:waypoint xsi:type="dc:Point" x="323" y="435" />
<di:waypoint xsi:type="dc:Point" x="376" y="435" />
<di:waypoint xsi:type="dc:Point" x="376" y="435" />
<di:waypoint xsi:type="dc:Point" x="407" y="435" />
<bpmndi:BPMNLabel>
<dc:Bounds x="346" y="425" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1j9tw48_di" bpmnElement="EndEvent_5">
<dc:Bounds x="883" y="417" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="856" y="453" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_10jatau_di" bpmnElement="SequenceFlow_8">
<di:waypoint xsi:type="dc:Point" x="792" y="435" />
<di:waypoint xsi:type="dc:Point" x="834" y="435" />
<di:waypoint xsi:type="dc:Point" x="834" y="435" />
<di:waypoint xsi:type="dc:Point" x="883" y="435" />
<bpmndi:BPMNLabel>
<dc:Bounds x="804" y="425" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="StartEvent_19uwuw1_di" bpmnElement="StartEvent_3">
<dc:Bounds x="432" y="371" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="405" y="407" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1y93al0_di" bpmnElement="Task_2">
<dc:Bounds x="525" y="397" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0gkhyqw_di" bpmnElement="SequenceFlow_10">
<di:waypoint xsi:type="dc:Point" x="468" y="389" />
<di:waypoint xsi:type="dc:Point" x="496" y="389" />
<di:waypoint xsi:type="dc:Point" x="496" y="437" />
<di:waypoint xsi:type="dc:Point" x="525" y="437" />
<bpmndi:BPMNLabel>
<dc:Bounds x="466" y="403" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1bmhzoz_di" bpmnElement="EndEvent_4">
<dc:Bounds x="683" y="419" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="656" y="455" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1s8bqj4_di" bpmnElement="SequenceFlow_11">
<di:waypoint xsi:type="dc:Point" x="625" y="437" />
<di:waypoint xsi:type="dc:Point" x="683" y="437" />
<bpmndi:BPMNLabel>
<dc:Bounds x="609" y="412" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="SubProcess_081736t_di" bpmnElement="SubProcess_3">
<dc:Bounds x="363" y="652" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_17rfv1e_di" bpmnElement="StartEvent_5">
<dc:Bounds x="573" y="674" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="546" y="710" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="AdHocSubProcess_0ojckgh_di" bpmnElement="SubProcess_4" isExpanded="false">
<dc:Bounds x="541" y="652" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="AdHocSubProcess_128w1vu_di" bpmnElement="SubProcess_2" isExpanded="true">
<dc:Bounds x="407" y="335" width="385" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SubProcess_0qs6p1k_di" bpmnElement="SubProcess_5" isExpanded="false">
<dc:Bounds x="716" y="652" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_0076vg5_di" bpmnElement="StartEvent_6">
<dc:Bounds x="495" y="537" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="468" y="573" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SubProcess_15na7ob_di" bpmnElement="SubProcess_6" isExpanded="true">
<dc:Bounds x="398" y="796" width="385" height="200" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,653 @@
'use strict';
require('../../../../TestHelper');
/* global bootstrapModeler, inject */
var modelingModule = require('../../../../../lib/features/modeling'),
coreModule = require('../../../../../lib/core');
var is = require('../../../../../lib/util/ModelUtil').is;
var testModules = [
modelingModule,
coreModule
];
describe('features/modeling - collapse and expand elements', function() {
var diagramXML = require('../../../../fixtures/bpmn/import/collapsed/processWithChildren.bpmn');
beforeEach(bootstrapModeler(diagramXML, {
modules: testModules
}));
describe('expand', function() {
var defaultSize = {
width: 350,
height: 200
};
it('collapsed-marker is removed',
inject(function(elementRegistry, bpmnReplace) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_3');
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
var businessObject = expandedSubProcess.businessObject;
// then +-marker is removed
expect(businessObject.di.isExpanded).to.eql(true);
})
);
it('show all children, but hide empty labels',
inject(function(elementRegistry, bpmnReplace) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_1');
var originalChildren = collapsedSubProcess.children.slice();
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then keep children
originalChildren.forEach(function(c) {
expect(expandedSubProcess.children).to.include(c);
});
// and show them
expect(expandedSubProcess.children).to.satisfy(allShown());
})
);
it('keep ad-hoc and multiInstance-marker',
inject(function(elementRegistry, bpmnReplace) {
// given
var collapsedAdHocSubProcess = elementRegistry.get('SubProcess_4');
// when
var expandedAdHocSubProcess = bpmnReplace.replaceElement(collapsedAdHocSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then
expect(is(expandedAdHocSubProcess, 'bpmn:AdHocSubProcess')).to.eql(true);
var businessObject = expandedAdHocSubProcess.businessObject;
expect(businessObject.loopCharacteristics).to.not.be.undefined;
})
);
describe('resizing', function() {
it('ignors hidden children',
inject(function(elementRegistry, bpmnReplace, eventBus) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_5');
var hiddenStartEvent = elementRegistry.get('StartEvent_6');
eventBus.once('commandStack.shape.toggleCollapse.postExecute', function(e) {
hiddenStartEvent.hidden = true;
});
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then hidden child should not be covered
expect(expandedSubProcess.x).to.be.greaterThan(hiddenStartEvent.x);
expect(expandedSubProcess.y).to.be.greaterThan(hiddenStartEvent.y);
})
);
it('without children is centered and has defaultBounds',
inject(function(elementRegistry, bpmnReplace) {
// given collapsed SubProcess without children
var collapsedSubProcess = elementRegistry.get('SubProcess_3');
var oldMid = {
x: collapsedSubProcess.x + collapsedSubProcess.width / 2,
y: collapsedSubProcess.y + collapsedSubProcess.height / 2
};
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then
var newMid = {
x: expandedSubProcess.x + expandedSubProcess.width / 2,
y: expandedSubProcess.y + expandedSubProcess.height / 2
};
expect(newMid).to.eql(oldMid);
expect(expandedSubProcess.width).to.be.at.least(defaultSize.width);
expect(expandedSubProcess.height).to.be.at.least(defaultSize.height);
})
);
it('with children is centered to childrenBoundingBox and has at least defaultBounds',
inject(function(elementRegistry, bpmnReplace) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_4');
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then
var startEvent = elementRegistry.get('StartEvent_5');
var midChildren = {
x: startEvent.x + startEvent.width / 2,
y: startEvent.y + startEvent.height / 2
};
var expandedMid = {
x: expandedSubProcess.x + expandedSubProcess.width / 2,
y: expandedSubProcess.y + expandedSubProcess.height / 2
};
expect(expandedMid).to.eql(midChildren),
expect(expandedSubProcess.width).to.be.at.least(defaultSize.width);
expect(expandedSubProcess.height).to.be.at.least(defaultSize.height);
})
);
it('to expanding collapsedSubProcess is coverd in childrenBoundingBox',
inject(function(elementRegistry, bpmnReplace) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_5');
var collapsedDownRightCorner = {
x: collapsedSubProcess.x + collapsedSubProcess.width,
y: collapsedSubProcess.y + collapsedSubProcess.height
};
// when
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then
var expandedDownRightCorner = {
x: expandedSubProcess.x + expandedSubProcess.width,
y: expandedSubProcess.y + expandedSubProcess.height
};
expect(expandedDownRightCorner.x).to.be.at.least(collapsedDownRightCorner.x);
expect(expandedDownRightCorner.y).to.be.at.least(collapsedDownRightCorner.y);
})
);
});
describe('undo', function() {
it('collapsed-marker is placed',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_1');
var expandedSubProcess = bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// when
commandStack.undo();
var businessObject = expandedSubProcess.businessObject;
// then +-marker is placed
expect(businessObject.di.isExpanded).to.eql(false);
})
);
it('restore previous bounds',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_1');
var originalBounds = {
x: collapsedSubProcess.x,
y: collapsedSubProcess.y,
width: collapsedSubProcess.width,
height: collapsedSubProcess.height
};
bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// when
commandStack.undo();
// then
expect(collapsedSubProcess).to.have.bounds(originalBounds);
})
);
it('hide children',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_1');
var originalChildren = collapsedSubProcess.children.slice();
bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// when
commandStack.undo();
// then keep children
originalChildren.forEach(function(c) {
expect(collapsedSubProcess.children).to.include(c);
});
// and hide them
expect(collapsedSubProcess.children).to.satisfy(allHidden());
})
);
});
});
describe('collapse', function() {
var defaultSize = {
width: 100,
height: 80
};
it('collapsed-marker is placed',
inject(function(elementRegistry, bpmnReplace) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
// when
var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
var businessObject = collapsedSubProcess.businessObject;
// then +-marker is set
expect(businessObject.di.isExpanded).to.eql(false);
})
);
it('hide all children',
inject(function(elementRegistry, bpmnReplace) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
var originalChildren = expandedSubProcess.children.slice();
// when
var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// then keep children
originalChildren.forEach(function(c) {
expect(collapsedSubProcess.children).to.include(c);
});
// and hide them
expect(collapsedSubProcess.children).to.satisfy(allHidden());
})
);
it('keep ad-hoc and multiInstance-marker',
inject(function(elementRegistry, bpmnReplace) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
// when
var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// then
expect(is(collapsedSubProcess, 'bpmn:AdHocSubProcess')).to.eql(true);
var businessObject = collapsedSubProcess.businessObject;
expect(businessObject.loopCharacteristics).to.not.be.undefined;
})
);
describe('resize', function() {
it('is centered and has default bounds',
inject(function(elementRegistry, bpmnReplace) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
var oldMid = {
x: expandedSubProcess.x + expandedSubProcess.width / 2,
y: expandedSubProcess.y + expandedSubProcess.height / 2
};
// when
var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// then
var newMid = {
x: collapsedSubProcess.x + collapsedSubProcess.width / 2,
y: collapsedSubProcess.y + collapsedSubProcess.height / 2
};
expect(newMid).to.eql(oldMid);
expect(collapsedSubProcess.width).to.be.at.least(defaultSize.width);
expect(collapsedSubProcess.height).to.be.at.least(defaultSize.height);
})
);
});
describe('undo', function() {
it('collapsed marker is removed',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// when
commandStack.undo();
var businessObject = collapsedSubProcess.businessObject;
// then +-marker is placed
expect(businessObject.di.isExpanded).to.eql(true);
})
);
it('originalBounds are restored',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
var originalBounds = {
x: expandedSubProcess.x,
y: expandedSubProcess.y,
width: expandedSubProcess.width,
height: expandedSubProcess.height
};
bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// when
commandStack.undo();
// then
expect(expandedSubProcess).to.have.bounds(originalBounds);
})
);
it('show children that were visible',
inject(function(elementRegistry, bpmnReplace, commandStack) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
var originalChildren = expandedSubProcess.children.slice();
bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// when
commandStack.undo();
// then keep children
originalChildren.forEach(function(c) {
expect(expandedSubProcess.children).to.include(c);
});
// and show the previously visible ones
expect(expandedSubProcess.children).to.satisfy(allShown());
})
);
});
});
describe('attaching marker', function() {
describe('collapsed', function() {
it('add ad-hoc-marker does not call toggleProvider',
inject(function(eventBus, bpmnReplace, elementRegistry) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_3');
// should not be called
eventBus.once('commandStack.shape.toggleCollapse.execute', function(e) {
expect(true).to.eql(false);
});
// when
bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:AdHocSubProcess',
isExpanded: false
}
);
// then
})
);
it('remove ad-hoc-marker does not call toggleProvider',
inject(function(eventBus, bpmnReplace, elementRegistry) {
// given
var collapsedSubProcess = elementRegistry.get('SubProcess_4');
// should not be called
eventBus.once('commandStack.shape.toggleCollapse.execute', function(e) {
expect(true).to.eql(false);
});
// when
bpmnReplace.replaceElement(collapsedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: false
}
);
// then
})
);
});
describe('expanded', function() {
it('add ad-hoc-marker does not call toggleProvider',
inject(function(eventBus, bpmnReplace, elementRegistry) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_6');
// should not be called
eventBus.once('commandStack.shape.toggleCollapse.execute', function(e) {
expect(true).to.eql(false);
});
// when
bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:AdHocSubProcess',
isExpanded: true
}
);
// then
})
);
it('remove ad-hoc-marker does not call toggleProvider',
inject(function(eventBus, bpmnReplace, elementRegistry) {
// given
var expandedSubProcess = elementRegistry.get('SubProcess_2');
// should not be called
eventBus.once('commandStack.shape.toggleCollapse.execute', function(e) {
expect(true).to.eql(false);
});
// when
bpmnReplace.replaceElement(expandedSubProcess,
{
type: 'bpmn:SubProcess',
isExpanded: true
}
);
// then
})
);
});
});
});
/////////// helpers /////////////////////////////
function allHidden() {
return childrenHidden(true);
}
function allShown() {
return childrenHidden(false);
}
function childrenHidden(hidden) {
return function(children) {
return children.every(function(child) {
// empty labels are allways hidden
if (child.type === 'label' && !child.businessObject.name) {
return child.hidden;
}
else {
return child.hidden == hidden;
}
});
};
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="0.7.0-dev">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:subProcess id="Task_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="SubProcess_0loe8m1_di" bpmnElement="Task_1">
<dc:Bounds x="149" y="128" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -1086,6 +1086,30 @@ describe('features/popup-menu - replace menu provider', function() {
});
describe('collapsed subprocesses', function() {
var diagramXML = require('./ReplaceMenuProvider.collapsedSubProcess.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('options do not include collapsed subprocesses itself', inject(function(elementRegistry, popupMenu) {
// given
var collapsedSubProcess = elementRegistry.get('Task_1');
// when
openPopup(collapsedSubProcess);
var collapsedSubProcessEntry = queryEntry(popupMenu, 'replace-with-collapsed-subprocess');
// then
expect(collapsedSubProcessEntry).to.not.exist;
}));
});
});

View File

@ -781,7 +781,7 @@ describe('features/replace - bpmn replace', function() {
});
describe('collapsed to expanded', function() {
describe('morph task with boundaryEvent', function() {
var diagramXML = require('../../../fixtures/bpmn/features/replace/01_replace.bpmn');
@ -790,7 +790,7 @@ describe('features/replace - bpmn replace', function() {
}));
it('should morph task -> expanded sub process', inject(function(bpmnReplace, elementRegistry) {
it('to expanded sub process', inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('Task_1');
@ -805,63 +805,32 @@ describe('features/replace - bpmn replace', function() {
// then
expect(is(newElement, 'bpmn:SubProcess')).to.be.true;
expect(isExpanded(newElement)).to.be.true;
}));
it('should expand sub-process', inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('SubProcessCollapsed');
var newElementData = {
type: 'bpmn:SubProcess',
isExpanded: true
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:SubProcess')).to.be.true;
expect(isExpanded(newElement)).to.be.true;
}));
it('should expand ad hoc sub-process', inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('AdHocSubProcessCollapsed');
var newElementData = {
type: 'bpmn:SubProcess',
isExpanded: true
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:AdHocSubProcess')).to.be.true;
expect(isExpanded(newElement)).to.be.true;
}));
it('should keep boundary events', inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('Task_1');
var newElementData = {
type: 'bpmn:SubProcess',
isExpanded: true
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:SubProcess')).to.be.true;
expect(isExpanded(newElement)).to.be.true;
// and keep boundaryEvent
expect(newElement.attachers.length).to.be.equal(2);
}));
it('to collapsed sub process', inject(function(bpmnReplace, elementRegistry) {
// given
var element = elementRegistry.get('Task_1');
var newElementData = {
type: 'bpmn:SubProcess',
isExpanded: false
};
// when
var newElement = bpmnReplace.replaceElement(element, newElementData);
// then
expect(is(newElement, 'bpmn:SubProcess')).to.be.true;
expect(isExpanded(newElement)).to.be.false;
// and keep boundaryEvent
expect(newElement.attachers.length).to.eql(2);
}));
});

View File

@ -0,0 +1,37 @@
<?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" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_QfpVMDO5Eeav0IX5Z32OQQ" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="Process_1" isExecutable="false">
<bpmn2:subProcess id="SubProcess_1">
<bpmn2:task id="Task_1">
<bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="SequenceFlow_1" name="" sourceRef="Task_1" targetRef="EndEvent_1"/>
<bpmn2:endEvent id="EndEvent_1">
<bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
</bpmn2:endEvent>
</bpmn2:subProcess>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_2" bpmnElement="SubProcess_1">
<dc:Bounds height="80.0" width="100.0" x="120.0" y="84.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_2" bpmnElement="Task_1">
<dc:Bounds height="80.0" width="100.0" x="55.0" y="84.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_2" bpmnElement="EndEvent_1">
<dc:Bounds height="36.0" width="36.0" x="249.0" y="106.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="267.0" y="147.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_1" bpmnElement="SequenceFlow_1" sourceElement="_BPMNShape_Task_2" targetElement="_BPMNShape_EndEvent_2">
<di:waypoint xsi:type="dc:Point" x="155.0" y="124.0"/>
<di:waypoint xsi:type="dc:Point" x="249.0" y="124.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="177.0" y="124.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>