From 1ede893679ed523776e6295e24f5c07e594e0787 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Thu, 2 Dec 2021 22:59:12 +0100 Subject: [PATCH] fix(modeling): handle close to {source,target} drop-on-flow Closes https://github.com/bpmn-io/bpmn-js/issues/1541 --- .../modeling/behavior/DropOnFlowBehavior.js | 4 +- .../behavior/DropOnFlowBehaviorSpec.js | 204 ++++++++++++++---- 2 files changed, 165 insertions(+), 43 deletions(-) diff --git a/lib/features/modeling/behavior/DropOnFlowBehavior.js b/lib/features/modeling/behavior/DropOnFlowBehavior.js index ef843a0a..b30c7ba7 100644 --- a/lib/features/modeling/behavior/DropOnFlowBehavior.js +++ b/lib/features/modeling/behavior/DropOnFlowBehavior.js @@ -59,12 +59,12 @@ export default function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid; // if last waypointBefore is inside shape's bounds, ignore docking point - if (!isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) { + if (waypointsBefore.length === 1 || !isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) { waypointsBefore.push(copy(dockingPoint)); } // if first waypointAfter is inside shape's bounds, ignore docking point - if (!isPointInsideBBox(shape, waypointsAfter[0])) { + if (waypointsAfter.length === 1 || !isPointInsideBBox(shape, waypointsAfter[0])) { waypointsAfter.unshift(copy(dockingPoint)); } } diff --git a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js index e3eb4333..fa3ceb6b 100644 --- a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js @@ -38,60 +38,182 @@ describe('modeling/behavior - drop on connection', function() { describe('create', function() { - it('should connect start -> target -> end', inject( - function(modeling, elementRegistry, elementFactory) { + describe('should connect start -> target -> end', function() { - // given - var intermediateThrowEvent = elementFactory.createShape({ - type: 'bpmn:IntermediateThrowEvent' - }); + it('connection middle', inject( + function(modeling, elementRegistry, elementFactory) { - var startEvent = elementRegistry.get('StartEvent'), - sequenceFlow = elementRegistry.get('SequenceFlow_1'), - task = elementRegistry.get('Task_1'); + // given + var intermediateThrowEvent = elementFactory.createShape({ + type: 'bpmn:IntermediateThrowEvent' + }); - var originalWaypoints = sequenceFlow.waypoints; + var startEvent = elementRegistry.get('StartEvent'), + sequenceFlow = elementRegistry.get('SequenceFlow_1'), + task = elementRegistry.get('Task_1'); - var dropPosition = { x: 340, y: 120 }; // first bendpoint + var originalWaypoints = sequenceFlow.waypoints; - // when - var newShape = modeling.createShape( - intermediateThrowEvent, - dropPosition, - sequenceFlow - ); + var dropPosition = { x: 340, y: 120 }; // first bendpoint - // then - var targetConnection = newShape.outgoing[0]; + // when + var newShape = modeling.createShape( + intermediateThrowEvent, + dropPosition, + sequenceFlow + ); - // new incoming connection - expect(newShape.incoming).to.have.length(1); - expect(newShape.incoming[0]).to.eql(sequenceFlow); + // then + var targetConnection = newShape.outgoing[0]; - // new outgoing connection - expect(newShape.outgoing).to.have.length(1); - expect(targetConnection).to.exist; - expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); + // new incoming connection + expect(newShape.incoming).to.have.length(1); + expect(newShape.incoming[0]).to.eql(sequenceFlow); - expect(startEvent.outgoing[0]).to.equal(newShape.incoming[0]); - expect(task.incoming[1]).to.equal(newShape.outgoing[0]); + // new outgoing connection + expect(newShape.outgoing).to.have.length(1); + expect(targetConnection).to.exist; + expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); - // split target at insertion point - expect(sequenceFlow).to.have.waypoints(flatten([ - originalWaypoints.slice(0, 1), - { x: 322, y: 120 } - ])); + expect(startEvent.outgoing[0]).to.equal(newShape.incoming[0]); + expect(task.incoming[1]).to.equal(newShape.outgoing[0]); - expect(sequenceFlow).to.have.endDocking(dropPosition); + // split target at insertion point + expect(sequenceFlow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 1), + { x: 322, y: 120 } + ])); + + expect(sequenceFlow).to.have.endDocking(dropPosition); + + expect(targetConnection).to.have.waypoints(flatten([ + { x: 340, y: 138 }, + originalWaypoints.slice(2) + ])); + + expect(targetConnection).to.have.startDocking(dropPosition); + } + )); + + + it('close to source', inject( + function(modeling, elementRegistry, elementFactory) { + + // given + var dropElement = elementFactory.createShape({ + type: 'bpmn:IntermediateThrowEvent' + }); + + var start = elementRegistry.get('Gateway_C'), + flow = elementRegistry.get('SequenceFlow_F'), + end = elementRegistry.get('Task_B'); + + var originalWaypoints = flow.waypoints.slice(); + + var dropPosition = { x: 495, y: 540 }; // overlapping source + + // when + var newShape = modeling.createShape( + dropElement, + dropPosition, + flow + ); + + // then + var targetConnection = newShape.outgoing[0]; + + // new incoming connection + expect(newShape.incoming).to.have.length(1); + expect(newShape.incoming[0]).to.equal(flow); + + // new outgoing connection + expect(newShape.outgoing).to.have.length(1); + expect(targetConnection).to.exist; + expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); + + expect(start.outgoing[0]).to.equal(newShape.incoming[0]); + expect(end.incoming[0]).to.equal(newShape.outgoing[0]); + + // split target at insertion point + expect(flow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 1), + [ + { x: 477, y: 540 } + ] + ])); + + expect(flow).to.have.endDocking(dropPosition); + + expect(targetConnection).to.have.waypoints(flatten([ + { x: 513, y: 540 }, + originalWaypoints.slice(1) + ])); + + expect(targetConnection).to.have.startDocking(dropPosition); + } + )); + + + it('close to target', inject( + function(modeling, elementRegistry, elementFactory) { + + // given + var dropElement = elementFactory.createShape({ + type: 'bpmn:IntermediateThrowEvent' + }); + + var start = elementRegistry.get('Gateway_C'), + flow = elementRegistry.get('SequenceFlow_F'), + end = elementRegistry.get('Task_B'); + + var originalWaypoints = flow.waypoints.slice(); + + var dropPosition = { x: 625, y: 540 }; // overlapping target + + // when + var newShape = modeling.createShape( + dropElement, + dropPosition, + flow + ); + + // then + var targetConnection = newShape.outgoing[0]; + + // new incoming connection + expect(newShape.incoming).to.have.length(1); + expect(newShape.incoming[0]).to.equal(flow); + + // new outgoing connection + expect(newShape.outgoing).to.have.length(1); + expect(targetConnection).to.exist; + expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); + + expect(start.outgoing[0]).to.equal(newShape.incoming[0]); + expect(end.incoming[0]).to.equal(newShape.outgoing[0]); + + // split target at insertion point + expect(flow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 1), + [ + { x: 607, y: 540 } + ] + ])); + + expect(flow).to.have.endDocking(dropPosition); + + expect(targetConnection).to.have.waypoints(flatten([ + { x: 643, y: 540 }, + originalWaypoints.slice(1) + ])); + + expect(targetConnection).to.have.startDocking(dropPosition); + } + )); + + }); - expect(targetConnection).to.have.waypoints(flatten([ - { x: 340, y: 138 }, - originalWaypoints.slice(2) - ])); - expect(targetConnection).to.have.startDocking(dropPosition); - } - )); it('should connect start -> target', inject(