Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lib/features/snapping/BpmnCreateMoveSnapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,33 @@ BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shap
snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent);
}

// When moving a label, FixHoverBehavior changes target to root element.
// If the label's owner (labelTarget) is inside a participant, we need to
// also include shapes from that participant as snap targets.
if (shape.labelTarget) {
var labelOwner = shape.labelTarget;
var participant = getParticipantAncestor(labelOwner);

if (participant && target !== participant) {

// Get snap targets from the participant
var participantSnapTargets = this.getSnapTargets(shape, participant);

// Add mid points for shapes and their labels
forEach(participantSnapTargets, function(snapTarget) {

// Add the shape's mid point
snapPoints.add('mid', getMid(snapTarget));

// Also add the shape's label's mid point if it has one
// (labels are not children of participant, so we need to add them explicitly)
if (snapTarget.label) {
snapPoints.add('mid', getMid(snapTarget.label));
}
});
}
}

return snapPoints;
};

Expand Down Expand Up @@ -287,3 +314,20 @@ function getDockingSnapOrigin(docking, isMove, event) {
y: docking.y
};
}

/**
* Get the participant ancestor of an element, if any.
*
* @param {Shape} element
*
* @return {Shape|null}
*/
function getParticipantAncestor(element) {
while (element) {
if (is(element, 'bpmn:Participant')) {
return element;
}
element = element.parent;
}
return null;
}
6 changes: 3 additions & 3 deletions test/spec/features/modeling/LabelBoundsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('label bounds', function() {
// then
var expectedX = getExpectedX(shape);

expect(shape.label.x).to.equal(expectedX);
expect(shape.label.x).to.be.closeTo(expectedX, DELTA);
}));


Expand Down Expand Up @@ -264,7 +264,7 @@ describe('label bounds', function() {
var xml = result.xml;

// strip spaces and line breaks after '>'
xml = xml.replace(/>\s+/g,'>');
xml = xml.replace(/>\s+/g, '>');

// get label width and height from XML
var matches = xml.match(/StartEvent_1_di.*?BPMNLabel.*?width="(\d*).*?height="(\d*)/);
Expand Down Expand Up @@ -308,7 +308,7 @@ describe('label bounds', function() {
var xml = result.xml;

// strip spaces and line breaks after '>'
xml = xml.replace(/>\s+/g,'>');
xml = xml.replace(/>\s+/g, '>');

// get label width and height from XML
var matches = xml.match(/StartEvent_3_di.*?BPMNLabel.*?width="(\d*).*?height="(\d*)/);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?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:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.1.0">
<bpmn:collaboration id="Collaboration_1">
<bpmn:participant id="Participant_1" processRef="Process_1" />
</bpmn:collaboration>
<bpmn:process id="Process_1" isExecutable="true">
<bpmn:laneSet id="LaneSet_1">
<bpmn:lane id="Lane_1">
<bpmn:flowNodeRef>StartEvent_1</bpmn:flowNodeRef>
<bpmn:flowNodeRef>Gateway_1</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="Lane_2" />
</bpmn:laneSet>
<bpmn:startEvent id="StartEvent_1" name="Start" />
<bpmn:exclusiveGateway id="Gateway_1" name="Decision" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="Participant_1_di" bpmnElement="Participant_1" isHorizontal="true">
<dc:Bounds x="100" y="100" width="600" height="250" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_1_di" bpmnElement="Lane_1" isHorizontal="true">
<dc:Bounds x="130" y="100" width="570" height="125" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_2_di" bpmnElement="Lane_2" isHorizontal="true">
<dc:Bounds x="130" y="225" width="570" height="125" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
<dc:Bounds x="182" y="142" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="188" y="185" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1_di" bpmnElement="Gateway_1" isMarkerVisible="true">
<dc:Bounds x="375" y="135" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="375" y="192" width="50" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
77 changes: 77 additions & 0 deletions test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,83 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() {
});


describe('label snapping inside participant', function() {

var diagramXML = require('./BpmnCreateMoveSnapping.label-snapping.bpmn');

beforeEach(bootstrapModeler(diagramXML, {
modules: testModules
}));


it('should add snap targets from participant when moving label', inject(
function(dragging, elementRegistry, move) {

// given
var startEvent = elementRegistry.get('StartEvent_1');
var startEventLabel = startEvent.label;

var labelMid = mid(startEventLabel);

// when
move.start(canvasEvent(labelMid), startEventLabel);

dragging.hover({ element: startEvent.parent, gfx: elementRegistry.getGraphics(startEvent.parent) });
dragging.move(canvasEventTopLeft({ x: 395, y: labelMid.y + 50 }, startEventLabel));
dragging.end();

// then
expect(mid(startEventLabel)).to.eql({
x: 400, // 395 snapped to 400
y: labelMid.y + 50
});
}
));

});


describe('label snapping in process (no participant)', function() {

var diagramXML = require('./BpmnCreateMoveSnapping.process.bpmn');

beforeEach(bootstrapModeler(diagramXML, {
modules: testModules
}));


it('should handle label move when no participant ancestor exists', inject(
function(dragging, elementRegistry, move, modeling, elementFactory, canvas) {

// given - create a StartEvent with external label in the process (no participant)
var process = elementRegistry.get('Process_1');
var startEvent = elementFactory.createShape({ type: 'bpmn:StartEvent' });

modeling.createShape(startEvent, { x: 300, y: 150 }, process);
modeling.updateLabel(startEvent, 'Start');

var startEventLabel = startEvent.label;

// Verify label was created
expect(startEventLabel).to.exist;
expect(startEventLabel.labelTarget).to.equal(startEvent);

var labelMid = mid(startEventLabel);

// when - move the label (getParticipantAncestor returns null for process)
// This exercises the code path where no participant ancestor exists
move.start(canvasEvent(labelMid), startEventLabel);
dragging.move(canvasEvent({ x: labelMid.x + 50, y: labelMid.y }));
dragging.end();

// then
expect(startEventLabel.x).to.exist;
}
));

});


describe('docking points', function() {

describe('move mode', function() {
Expand Down