fix(modeling): support newBounds

This fixes a bug where Modeling#updateLabel would not work on
text annotations, because these need to have labels pre-configured.

* Modeling#updateLabel now takes the (optional) newBounds
* newBounds must now explicitly be passed to trigger resize
  for text annotations
* newBounds is _only_ passed for text annotations via
  LabelEditingProvider (it was discarded before anyway)
* lib/features/label-editing did not depend on lib/features/modeling
  for historical reasons. It now uses the offical #updateLabel
  API provided by Modeling
* Localize test diagrams

Closes #753
This commit is contained in:
Nico Rehwaldt 2018-01-24 20:25:28 +01:00 committed by Philipp Fromme
parent d32da90013
commit f1daf4841f
14 changed files with 565 additions and 410 deletions

View File

@ -5,11 +5,11 @@ module.exports = {
require('diagram-js/lib/features/hand-tool'), require('diagram-js/lib/features/hand-tool'),
require('diagram-js/lib/features/lasso-tool'), require('diagram-js/lib/features/lasso-tool'),
require('diagram-js/lib/features/space-tool'), require('diagram-js/lib/features/space-tool'),
require('diagram-js-direct-editing'),
require('../global-connect'), require('../global-connect'),
require('../copy-paste'), require('../copy-paste'),
require('../distribute-elements'), require('../distribute-elements'),
require('../search'), require('../search')
require('../modeling')
], ],
editorActions: [ 'type', require('./BpmnEditorActions') ] editorActions: [ 'type', require('./BpmnEditorActions') ]
}; };

View File

@ -2,8 +2,6 @@
var assign = require('lodash/object/assign'); var assign = require('lodash/object/assign');
var UpdateLabelHandler = require('./cmd/UpdateLabelHandler');
var LabelUtil = require('./LabelUtil'); var LabelUtil = require('./LabelUtil');
var is = require('../../util/ModelUtil').is, var is = require('../../util/ModelUtil').is,
@ -15,22 +13,27 @@ var SMALL_FONT_SIZE = 11,
MEDIUM_FONT_SIZE = 12, MEDIUM_FONT_SIZE = 12,
MEDIUM_LINE_HEIGHT = 14; MEDIUM_LINE_HEIGHT = 14;
function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, resizeHandles) {
function LabelEditingProvider(
eventBus, canvas, directEditing,
modeling, resizeHandles) {
this._canvas = canvas; this._canvas = canvas;
this._commandStack = commandStack; this._modeling = modeling;
directEditing.registerProvider(this); directEditing.registerProvider(this);
commandStack.registerHandler('element.updateLabel', UpdateLabelHandler);
// listen to dblclick on non-root elements // listen to dblclick on non-root elements
eventBus.on('element.dblclick', function(event) { eventBus.on('element.dblclick', function(event) {
activateDirectEdit(event.element, true); activateDirectEdit(event.element, true);
}); });
// complete on followup canvas operation // complete on followup canvas operation
eventBus.on([ 'element.mousedown', 'drag.init', 'canvas.viewbox.changing' ], function(event) { eventBus.on([
'element.mousedown',
'drag.init',
'canvas.viewbox.changing'
], function(event) {
directEditing.complete(); directEditing.complete();
}); });
@ -50,12 +53,14 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, res
canExecute = event.context.canExecute, canExecute = event.context.canExecute,
isTouch = event.isTouch; isTouch = event.isTouch;
// TODO(nikku): we need to find a way to support the direct editing // TODO(nikku): we need to find a way to support the
// on mobile devices; right now this will break for desworkflowediting on mobile devices // direct editing on mobile devices; right now this will
// break for desworkflowediting on mobile devices
// as it breaks the user interaction workflow // as it breaks the user interaction workflow
// TODO(nre): we should temporarily focus the edited element here // TODO(nre): we should temporarily focus the edited element
// and release the focused viewport after the direct edit operation is finished // here and release the focused viewport after the direct edit
// operation is finished
if (isTouch) { if (isTouch) {
return; return;
} }
@ -83,7 +88,13 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, res
} }
LabelEditingProvider.$inject = [ 'eventBus', 'canvas', 'directEditing', 'commandStack', 'resizeHandles' ]; LabelEditingProvider.$inject = [
'eventBus',
'canvas',
'directEditing',
'modeling',
'resizeHandles'
];
module.exports = LabelEditingProvider; module.exports = LabelEditingProvider;
@ -119,8 +130,15 @@ LabelEditingProvider.prototype.activate = function(element) {
var options = {}; var options = {};
// tasks // tasks
if (isAny(element, [ 'bpmn:Task', 'bpmn:Participant', 'bpmn:Lane', 'bpmn:CallActivity' ]) || if (
isCollapsedSubProcess(element)) { isAny(element, [
'bpmn:Task',
'bpmn:Participant',
'bpmn:Lane',
'bpmn:CallActivity'
]) ||
isCollapsedSubProcess(element)
) {
assign(options, { assign(options, {
centerVertically: true centerVertically: true
}); });
@ -154,7 +172,8 @@ LabelEditingProvider.prototype.activate = function(element) {
* *
* @param {djs.model.Base} element * @param {djs.model.Base} element
* *
* @return {Object} an object containing information about position and size (fixed or minimum and/or maximum) * @return {Object} an object containing information about position
* and size (fixed or minimum and/or maximum)
*/ */
LabelEditingProvider.prototype.getEditingBBox = function(element) { LabelEditingProvider.prototype.getEditingBBox = function(element) {
var canvas = this._canvas; var canvas = this._canvas;
@ -203,7 +222,8 @@ LabelEditingProvider.prototype.getEditingBBox = function(element) {
} }
// internal labels for tasks and collapsed call activities, sub processes and participants // internal labels for tasks and collapsed call activities,
// sub processes and participants
if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) || if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) ||
isCollapsedPool(element) || isCollapsedPool(element) ||
isCollapsedSubProcess(element)) { isCollapsedSubProcess(element)) {
@ -288,19 +308,26 @@ LabelEditingProvider.prototype.getEditingBBox = function(element) {
}; };
LabelEditingProvider.prototype.update = function(element, newLabel, activeContextText, bounds) { LabelEditingProvider.prototype.update = function(
var absoluteElementBBox = this._canvas.getAbsoluteBBox(element); element, newLabel,
activeContextText, bounds) {
this._commandStack.execute('element.updateLabel', { var newBounds,
element: element, bbox;
newLabel: newLabel,
bounds: { if (is(element, 'bpmn:TextAnnotation')) {
bbox = this._canvas.getAbsoluteBBox(element);
newBounds = {
x: element.x, x: element.x,
y: element.y, y: element.y,
width: element.width / absoluteElementBBox.width * bounds.width, width: element.width / bbox.width * bounds.width,
height: element.height / absoluteElementBBox.height * bounds.height height: element.height / bbox.height * bounds.height
} };
}); }
this._modeling.updateLabel(element, newLabel, newBounds);
}; };

View File

@ -54,7 +54,7 @@ function UpdateLabelHandler(modeling) {
function postExecute(ctx) { function postExecute(ctx) {
var element = ctx.element, var element = ctx.element,
label = element.label || element, label = element.label || element,
bounds = ctx.bounds; newBounds = ctx.newBounds;
// ignore internal labels for elements except text annotations // ignore internal labels for elements except text annotations
if (!hasExternalLabel(element) && !is(element, 'bpmn:TextAnnotation')) { if (!hasExternalLabel(element) && !is(element, 'bpmn:TextAnnotation')) {
@ -65,15 +65,21 @@ function UpdateLabelHandler(modeling) {
var text = bo.name || bo.text; var text = bo.name || bo.text;
// don't resize without text
if (!text) { if (!text) {
return; return;
} }
// get layouted text bounds and resize external // resize element based on label _or_ pre-defined bounds
// external label accordingly if (typeof newBounds === 'undefined') {
var newBounds = is(element, 'bpmn:TextAnnotation') ? bounds : getLayoutedBounds(label, text, textUtil); newBounds = getLayoutedBounds(label, text, textUtil);
}
modeling.resizeShape(label, newBounds, NULL_DIMENSIONS); // setting newBounds to false or _null_ will
// disable the postExecute resize operation
if (newBounds) {
modeling.resizeShape(label, newBounds, NULL_DIMENSIONS);
}
} }
// API // API

View File

@ -1,6 +1,5 @@
module.exports = { module.exports = {
__depends__: [ __depends__: [
require('diagram-js/lib/command'),
require('diagram-js/lib/features/change-support'), require('diagram-js/lib/features/change-support'),
require('diagram-js/lib/features/resize'), require('diagram-js/lib/features/resize'),
require('diagram-js-direct-editing') require('diagram-js-direct-editing')

View File

@ -13,6 +13,8 @@ var UpdatePropertiesHandler = require('./cmd/UpdatePropertiesHandler'),
IdClaimHandler = require('./cmd/IdClaimHandler'), IdClaimHandler = require('./cmd/IdClaimHandler'),
SetColorHandler = require('./cmd/SetColorHandler'); SetColorHandler = require('./cmd/SetColorHandler');
var UpdateLabelHandler = require('../label-editing/cmd/UpdateLabelHandler');
/** /**
* BPMN 2.0 modeling features activator * BPMN 2.0 modeling features activator
@ -46,15 +48,17 @@ Modeling.prototype.getHandlers = function() {
handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler; handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler;
handlers['id.updateClaim'] = IdClaimHandler; handlers['id.updateClaim'] = IdClaimHandler;
handlers['element.setColor'] = SetColorHandler; handlers['element.setColor'] = SetColorHandler;
handlers['element.updateLabel'] = UpdateLabelHandler;
return handlers; return handlers;
}; };
Modeling.prototype.updateLabel = function(element, newLabel) { Modeling.prototype.updateLabel = function(element, newLabel, newBounds) {
this._commandStack.execute('element.updateLabel', { this._commandStack.execute('element.updateLabel', {
element: element, element: element,
newLabel: newLabel newLabel: newLabel,
newBounds: newBounds
}); });
}; };

View File

@ -2,7 +2,6 @@ module.exports = {
__init__: [ 'modeling', 'bpmnUpdater' ], __init__: [ 'modeling', 'bpmnUpdater' ],
__depends__: [ __depends__: [
require('./behavior'), require('./behavior'),
require('../label-editing'),
require('../rules'), require('../rules'),
require('../ordering'), require('../ordering'),
require('../replace'), require('../replace'),

View File

@ -157,7 +157,8 @@ describe('features/auto-place', function() {
coreModule, coreModule,
modelingModule, modelingModule,
autoPlaceModule, autoPlaceModule,
selectionModule selectionModule,
labelEditingModule
] ]
})); }));

View File

@ -28,7 +28,14 @@ describe('features/editor-actions', function() {
return function() { return function() {
beforeEach(bootstrapModeler(xml, { modules: [ bpmnEditorActionsModule, modelingModule, coreModule ] })); beforeEach(bootstrapModeler(xml, {
modules: [
bpmnEditorActionsModule,
modelingModule,
coreModule
]
}));
it('should move to origin', inject(function(editorActions) { it('should move to origin', inject(function(editorActions) {
// given // given
@ -43,28 +50,42 @@ describe('features/editor-actions', function() {
// then // then
expect(pick(boundingBox, [ 'x', 'y' ])).to.eql({ x: 0, y: 0 }); expect(pick(boundingBox, [ 'x', 'y' ])).to.eql({ x: 0, y: 0 });
})); }));
}; };
} }
describe('single process', testMoveToOrigin(basicXML)); describe('single process', testMoveToOrigin(basicXML));
describe('collaboration', testMoveToOrigin(collaborationXML)); describe('collaboration', testMoveToOrigin(collaborationXML));
describe('subprocesses', function() { describe('subprocesses', function() {
beforeEach(bootstrapModeler(basicXML, { modules: [ bpmnEditorActionsModule, modelingModule, coreModule ] })); beforeEach(bootstrapModeler(basicXML, {
modules: [
it('should ignore children of subprocesses', inject(function(editorActions, elementRegistry) { bpmnEditorActionsModule,
// given modelingModule,
var startEvent = elementRegistry.get('StartEvent_3'), coreModule
startEventParent = getParent(startEvent); ]
// when
editorActions.trigger('moveToOrigin');
// then
expect(getParent(startEvent)).to.equal(startEventParent);
})); }));
it('should ignore children of subprocesses', inject(
function(editorActions, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_3'),
startEventParent = getParent(startEvent);
// when
editorActions.trigger('moveToOrigin');
// then
expect(getParent(startEvent)).to.equal(startEventParent);
}
));
}); });
}); });

View File

@ -7,129 +7,147 @@ var pick = require('lodash/object/pick');
var labelEditingModule = require('lib/features/label-editing'), var labelEditingModule = require('lib/features/label-editing'),
coreModule = require('lib/core'), coreModule = require('lib/core'),
draggingModule = require('diagram-js/lib/features/dragging'), draggingModule = require('diagram-js/lib/features/dragging'),
modelingModule = require('diagram-js/lib/features/modeling'); modelingModule = require('lib/features/modeling');
describe('features - label-editing preview', function() { describe('features - label-editing preview', function() {
var diagramXML = require('../../../fixtures/bpmn/features/label-editing/labels.bpmn'); var diagramXML = require('./LabelEditing.bpmn');
var testModules = [ labelEditingModule, coreModule, draggingModule, modelingModule ]; beforeEach(bootstrapViewer(diagramXML, {
modules: [
labelEditingModule,
coreModule,
draggingModule,
modelingModule
]
}));
beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
describe('activate', function() { describe('activate', function() {
it('[external labels AND text annotations ] should add marker to hide element on activate', inject(function(directEditing, elementRegistry) { it('[external labels AND text annotations ] should add marker to hide element on activate', inject(
function(directEditing, elementRegistry) {
// given // given
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
// when // when
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
// then // then
var gfx = elementRegistry.getGraphics(textAnnotation); var gfx = elementRegistry.getGraphics(textAnnotation);
expect(gfx.classList.contains('djs-element-hidden')).to.be.true; expect(gfx.classList.contains('djs-element-hidden')).to.be.true;
})); }
));
it('[internal labels] should add marker to hide label on activate', inject(function(directEditing, elementRegistry) { it('[internal labels] should add marker to hide label on activate', inject(
function(directEditing, elementRegistry) {
// given // given
var task = elementRegistry.get('Task_1'); var task = elementRegistry.get('Task_1');
// when // when
directEditing.activate(task); directEditing.activate(task);
// then // then
var gfx = elementRegistry.getGraphics(task); var gfx = elementRegistry.getGraphics(task);
expect(gfx.classList.contains('djs-label-hidden')).to.be.true; expect(gfx.classList.contains('djs-label-hidden')).to.be.true;
})); }
));
}); });
describe('resize', function() { describe('resize', function() {
it('[text annotations] should resize preview on resize', inject(function(directEditing, elementRegistry, eventBus, labelEditingPreview) { it('[text annotations] should resize preview on resize', inject(
function(directEditing, elementRegistry, eventBus, labelEditingPreview) {
// given // given
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
// when // when
eventBus.fire('directEditing.resize', { eventBus.fire('directEditing.resize', {
width: 200, width: 200,
height: 200, height: 200,
dx: 100, dx: 100,
dy: 100 dy: 100
}); });
// then // then
var bounds = bbox(labelEditingPreview.path); var bounds = bbox(labelEditingPreview.path);
expect(bounds).to.eql({ x: 0, y: 0, width: 10, height: 300 }); expect(bounds).to.eql({ x: 0, y: 0, width: 10, height: 300 });
})); }
));
it('[text annotations] should resize preview below 0', inject(function(directEditing, elementRegistry, eventBus, labelEditingPreview) { it('[text annotations] should resize preview below 0', inject(
function(directEditing, elementRegistry, eventBus, labelEditingPreview) {
// given // given
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
// when // when
eventBus.fire('directEditing.resize', { eventBus.fire('directEditing.resize', {
width: 200, width: 200,
height: 200, height: 200,
dx: -300, dx: -300,
dy: -300 dy: -300
}); });
// then // then
var bounds = bbox(labelEditingPreview.path); var bounds = bbox(labelEditingPreview.path);
expect(bounds).to.eql({ x: 0, y: 0, width: 10, height: 0 }); expect(bounds).to.eql({ x: 0, y: 0, width: 10, height: 0 });
})); }
));
}); });
describe('complete/cancel', function() { describe('complete/cancel', function() {
it('[external labels AND text annotations] should remove marker to hide element on complete', inject(function(directEditing, elementRegistry) { it('[external labels AND text annotations] should remove marker to hide element on complete', inject(
function(directEditing, elementRegistry) {
// given // given
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
// when // when
directEditing.complete(); directEditing.complete();
// then // then
var gfx = elementRegistry.getGraphics(textAnnotation); var gfx = elementRegistry.getGraphics(textAnnotation);
expect(gfx.classList.contains('djs-element-hidden')).to.be.false; expect(gfx.classList.contains('djs-element-hidden')).to.be.false;
})); }
));
it('[internal labels] should remove marker to hide label on complete', inject(function(directEditing, elementRegistry) { it('[internal labels] should remove marker to hide label on complete', inject(
function(directEditing, elementRegistry) {
// given // given
var task = elementRegistry.get('Task_1'); var task = elementRegistry.get('Task_1');
directEditing.activate(task); directEditing.activate(task);
// when // when
directEditing.complete(); directEditing.complete();
// then // then
var gfx = elementRegistry.getGraphics(task); var gfx = elementRegistry.getGraphics(task);
expect(gfx.classList.contains('djs-label-hidden')).to.be.false; expect(gfx.classList.contains('djs-label-hidden')).to.be.false;
})); }
));
}); });

View File

@ -6,7 +6,7 @@
var labelEditingModule = require('lib/features/label-editing'), var labelEditingModule = require('lib/features/label-editing'),
coreModule = require('lib/core'), coreModule = require('lib/core'),
draggingModule = require('diagram-js/lib/features/dragging'), draggingModule = require('diagram-js/lib/features/dragging'),
modelingModule = require('diagram-js/lib/features/modeling'); modelingModule = require('lib/features/modeling');
var LabelUtil = require('lib/features/label-editing/LabelUtil'); var LabelUtil = require('lib/features/label-editing/LabelUtil');
@ -36,118 +36,138 @@ function expectBounds(parent, bounds) {
describe('features - label-editing', function() { describe('features - label-editing', function() {
var diagramXML = require('../../../fixtures/bpmn/features/label-editing/labels.bpmn'); var diagramXML = require('./LabelEditing.bpmn');
describe('basics', function() { describe('basics', function() {
var testModules = [ labelEditingModule, coreModule, draggingModule, modelingModule ]; beforeEach(bootstrapViewer(diagramXML, {
modules: [
beforeEach(bootstrapViewer(diagramXML, { modules: testModules })); labelEditingModule,
coreModule,
draggingModule,
it('should register on dblclick', inject(function(elementRegistry, directEditing, eventBus) { modelingModule
]
// given
var shape = elementRegistry.get('Task_1');
// when
eventBus.fire('element.dblclick', { element: shape });
// then
expect(directEditing.isActive()).to.be.true;
// clean up
directEditing._textbox.destroy();
})); }));
it('should cancel on <ESC>', inject(function(elementRegistry, directEditing, eventBus) { it('should register on dblclick', inject(
function(elementRegistry, directEditing, eventBus) {
// given // given
var shape = elementRegistry.get('Task_1'), var shape = elementRegistry.get('Task_1');
task = shape.businessObject;
var oldName = task.name; // when
eventBus.fire('element.dblclick', { element: shape });
// activate // then
eventBus.fire('element.dblclick', { element: shape }); expect(directEditing.isActive()).to.be.true;
var textbox = directEditing._textbox.content; // clean up
directEditing._textbox.destroy();
// when }
// change + ESC is pressed ));
textbox.innerText = 'new value';
triggerKeyEvent(textbox, 'keydown', 27);
// then
expect(directEditing.isActive()).to.be.false;
expect(task.name).to.equal(oldName);
}));
it('should complete on drag start', inject(function(elementRegistry, directEditing, dragging) { it('should cancel on <ESC>', inject(
function(elementRegistry, directEditing, eventBus) {
// given // given
var shape = elementRegistry.get('Task_1'), var shape = elementRegistry.get('Task_1'),
task = shape.businessObject; task = shape.businessObject;
directEditing.activate(shape); var oldName = task.name;
directEditing._textbox.content.textContent = 'FOO BAR'; // activate
eventBus.fire('element.dblclick', { element: shape });
// when var textbox = directEditing._textbox.content;
dragging.init(null, { x: 0, y: 0 }, 'foo');
// then // when
expect(task.name).to.equal('FOO BAR'); // change + ESC is pressed
})); textbox.innerText = 'new value';
triggerKeyEvent(textbox, 'keydown', 27);
// then
expect(directEditing.isActive()).to.be.false;
expect(task.name).to.equal(oldName);
}
));
it('should submit on root element click', inject(function(elementRegistry, directEditing, canvas, eventBus) { it('should complete on drag start', inject(
function(elementRegistry, directEditing, dragging) {
// given // given
var shape = elementRegistry.get('Task_1'), var shape = elementRegistry.get('Task_1'),
task = shape.businessObject; task = shape.businessObject;
// activate directEditing.activate(shape);
eventBus.fire('element.dblclick', { element: shape });
var newName = 'new value'; directEditing._textbox.content.textContent = 'FOO BAR';
// a <textarea /> element // when
var content = directEditing._textbox.content; dragging.init(null, { x: 0, y: 0 }, 'foo');
content.innerText = newName; // then
expect(task.name).to.equal('FOO BAR');
}
));
// when
// change + <element.mousedown>
eventBus.fire('element.mousedown', { element: canvas.getRootElement() }); it('should submit on root element click', inject(
function(elementRegistry, directEditing, canvas, eventBus) {
// then // given
expect(directEditing.isActive()).to.be.false; var shape = elementRegistry.get('Task_1'),
expect(task.name).to.equal(newName); task = shape.businessObject;
}));
// activate
eventBus.fire('element.dblclick', { element: shape });
var newName = 'new value';
// a <textarea /> element
var content = directEditing._textbox.content;
content.innerText = newName;
// when
// change + <element.mousedown>
eventBus.fire('element.mousedown', { element: canvas.getRootElement() });
// then
expect(directEditing.isActive()).to.be.false;
expect(task.name).to.equal(newName);
}
));
}); });
describe('details', function() { describe('details', function() {
var testModules = [ labelEditingModule, coreModule, modelingModule ]; beforeEach(bootstrapViewer(diagramXML, {
modules: [
beforeEach(bootstrapViewer(diagramXML, { modules: testModules })); labelEditingModule,
coreModule,
modelingModule
]
}));
var elementRegistry, var elementRegistry,
eventBus, eventBus,
directEditing; directEditing;
beforeEach(inject([ 'elementRegistry', 'eventBus', 'directEditing', function(_elementRegistry, _eventBus, _directEditing) { beforeEach(inject([
elementRegistry = _elementRegistry; 'elementRegistry', 'eventBus', 'directEditing',
eventBus = _eventBus; function(_elementRegistry, _eventBus, _directEditing) {
directEditing = _directEditing; elementRegistry = _elementRegistry;
}])); eventBus = _eventBus;
directEditing = _directEditing;
}
]));
function directEditActivate(element) { function directEditActivate(element) {
@ -351,11 +371,12 @@ describe('features - label-editing', function() {
describe('sizes', function() { describe('sizes', function() {
var testModules = [ labelEditingModule, coreModule, modelingModule ];
beforeEach(bootstrapViewer(diagramXML, { beforeEach(bootstrapViewer(diagramXML, {
modules: testModules, modules: [
labelEditingModule,
coreModule,
modelingModule
],
canvas: { deferUpdate: false } canvas: { deferUpdate: false }
})); }));
@ -364,268 +385,292 @@ describe('features - label-editing', function() {
describe('external labels', function() { describe('external labels', function() {
it('[zoom 1] should have fixed width and element height', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have fixed width and element height', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var startEvent = elementRegistry.get('StartEvent_1'); var startEvent = elementRegistry.get('StartEvent_1');
var bounds = canvas.getAbsoluteBBox(startEvent.label); var bounds = canvas.getAbsoluteBBox(startEvent.label);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(startEvent); directEditing.activate(startEvent);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: mid.x - (45 * zoom), x: mid.x - (45 * zoom),
y: bounds.y - (7 * zoom), y: bounds.y - (7 * zoom),
width: (90 * zoom), width: (90 * zoom),
height: bounds.height + (5 * zoom) + 7 height: bounds.height + (5 * zoom) + 7
}); });
})); }
));
it('[zoom 1.5] should have fixed width and element height', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have fixed width and element height', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var startEvent = elementRegistry.get('StartEvent_1'); var startEvent = elementRegistry.get('StartEvent_1');
var bounds = canvas.getAbsoluteBBox(startEvent.label); var bounds = canvas.getAbsoluteBBox(startEvent.label);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(startEvent); directEditing.activate(startEvent);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: mid.x - (45 * zoom), x: mid.x - (45 * zoom),
y: bounds.y - (7 * zoom), y: bounds.y - (7 * zoom),
width: (90 * zoom), width: (90 * zoom),
height: bounds.height + (5 * zoom) + (7 * zoom) height: bounds.height + (5 * zoom) + (7 * zoom)
}); });
})); }
));
}); });
describe('internal labels', function() { describe('internal labels', function() {
it('[zoom 1] should have element size', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have element size', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var task = elementRegistry.get('Task_1'); var task = elementRegistry.get('Task_1');
var bounds = canvas.getAbsoluteBBox(task); var bounds = canvas.getAbsoluteBBox(task);
directEditing.activate(task); directEditing.activate(task);
expectBounds(directEditing._textbox.parent, bounds); expectBounds(directEditing._textbox.parent, bounds);
})); }
));
it('[zoom 1.5] should have element size', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have element size', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var task = elementRegistry.get('Task_1'); var task = elementRegistry.get('Task_1');
var bounds = canvas.getAbsoluteBBox(task); var bounds = canvas.getAbsoluteBBox(task);
directEditing.activate(task); directEditing.activate(task);
expectBounds(directEditing._textbox.parent, bounds); expectBounds(directEditing._textbox.parent, bounds);
})); }
));
}); });
describe('sequence flows', function() { describe('sequence flows', function() {
it('[zoom 1] should have fixed width and element height', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have fixed width and element height', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var sequenceFlow = elementRegistry.get('SequenceFlow_1'); var sequenceFlow = elementRegistry.get('SequenceFlow_1');
var bounds = canvas.getAbsoluteBBox(sequenceFlow.label); var bounds = canvas.getAbsoluteBBox(sequenceFlow.label);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(sequenceFlow); directEditing.activate(sequenceFlow);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: mid.x - (45 * zoom), x: mid.x - (45 * zoom),
y: bounds.y - (7 * zoom), y: bounds.y - (7 * zoom),
width: (90 * zoom), width: (90 * zoom),
height: bounds.height + (5 * zoom) + 7 height: bounds.height + (5 * zoom) + 7
}); });
})); }
));
it('[zoom 1.5] should have fixed width and element height', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have fixed width and element height', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var sequenceflow = elementRegistry.get('SequenceFlow_1'); var sequenceflow = elementRegistry.get('SequenceFlow_1');
var bounds = canvas.getAbsoluteBBox(sequenceflow.label); var bounds = canvas.getAbsoluteBBox(sequenceflow.label);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(sequenceflow); directEditing.activate(sequenceflow);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: mid.x - (45 * zoom), x: mid.x - (45 * zoom),
y: bounds.y - (7 * zoom), y: bounds.y - (7 * zoom),
width: (90 * zoom), width: (90 * zoom),
height: bounds.height + (5 * zoom) + (7 * zoom) height: bounds.height + (5 * zoom) + (7 * zoom)
}); });
})); }
));
}); });
describe('text annotations', function() { describe('text annotations', function() {
it('[zoom 1] should have element size', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have element size', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
var bounds = canvas.getAbsoluteBBox(textAnnotation); var bounds = canvas.getAbsoluteBBox(textAnnotation);
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
expectBounds(directEditing._textbox.parent, bounds); expectBounds(directEditing._textbox.parent, bounds);
})); }
));
it('[zoom 1.5] should have element size', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have element size', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var textAnnotation = elementRegistry.get('TextAnnotation_1'); var textAnnotation = elementRegistry.get('TextAnnotation_1');
var bounds = canvas.getAbsoluteBBox(textAnnotation); var bounds = canvas.getAbsoluteBBox(textAnnotation);
directEditing.activate(textAnnotation); directEditing.activate(textAnnotation);
expectBounds(directEditing._textbox.parent, bounds); expectBounds(directEditing._textbox.parent, bounds);
})); }
));
}); });
describe('expanded sub processes', function() { describe('expanded sub processes', function() {
it('[zoom 1] should have element width and height to fit text', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have element width and height to fit text', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var subProcess = elementRegistry.get('SubProcess_1'); var subProcess = elementRegistry.get('SubProcess_1');
var bounds = canvas.getAbsoluteBBox(subProcess); var bounds = canvas.getAbsoluteBBox(subProcess);
directEditing.activate(subProcess); directEditing.activate(subProcess);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: bounds.x, x: bounds.x,
y: bounds.y, y: bounds.y,
width: bounds.width, width: bounds.width,
height: (MEDIUM_LINE_HEIGHT * zoom) + (7 * 2 * zoom) height: (MEDIUM_LINE_HEIGHT * zoom) + (7 * 2 * zoom)
}); });
})); }
));
it('[zoom 1.5] should have element width and height to fit text', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have element width and height to fit text', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var subProcess = elementRegistry.get('SubProcess_1'); var subProcess = elementRegistry.get('SubProcess_1');
var bounds = canvas.getAbsoluteBBox(subProcess); var bounds = canvas.getAbsoluteBBox(subProcess);
directEditing.activate(subProcess); directEditing.activate(subProcess);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: bounds.x, x: bounds.x,
y: bounds.y, y: bounds.y,
width: bounds.width, width: bounds.width,
height: (MEDIUM_LINE_HEIGHT * zoom) + (7 * 2 * zoom) height: (MEDIUM_LINE_HEIGHT * zoom) + (7 * 2 * zoom)
}); });
})); }
));
}); });
describe('pools/lanes', function() { describe('pools/lanes', function() {
it('[zoom 1] should have width of element height, height of 30', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1] should have width of element height, height of 30', inject(
var zoom = 1; function(canvas, directEditing, elementRegistry) {
var zoom = 1;
canvas.zoom(zoom); canvas.zoom(zoom);
var pool = elementRegistry.get('Participant_1'); var pool = elementRegistry.get('Participant_1');
var bounds = canvas.getAbsoluteBBox(pool); var bounds = canvas.getAbsoluteBBox(pool);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(pool); directEditing.activate(pool);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: bounds.x - (bounds.height / 2) + (15 * zoom), x: bounds.x - (bounds.height / 2) + (15 * zoom),
y: mid.y - (30 * zoom) / 2, y: mid.y - (30 * zoom) / 2,
width: bounds.height * zoom, width: bounds.height * zoom,
height: 30 * zoom height: 30 * zoom
}); });
})); }
));
it('[zoom 1.5] should have width of element height, height of 30', inject(function(canvas, directEditing, elementRegistry) { it('[zoom 1.5] should have width of element height, height of 30', inject(
var zoom = 1.5; function(canvas, directEditing, elementRegistry) {
var zoom = 1.5;
canvas.zoom(zoom); canvas.zoom(zoom);
var pool = elementRegistry.get('Participant_1'); var pool = elementRegistry.get('Participant_1');
var bounds = canvas.getAbsoluteBBox(pool); var bounds = canvas.getAbsoluteBBox(pool);
var mid = { var mid = {
x: bounds.x + bounds.width / 2, x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2 y: bounds.y + bounds.height / 2
}; };
directEditing.activate(pool); directEditing.activate(pool);
expectBounds(directEditing._textbox.parent, { expectBounds(directEditing._textbox.parent, {
x: bounds.x - (bounds.height / 2) + (15 * zoom), x: bounds.x - (bounds.height / 2) + (15 * zoom),
y: mid.y - (30 * zoom) / 2, y: mid.y - (30 * zoom) / 2,
width: bounds.height, width: bounds.height,
height: 30 * zoom height: 30 * zoom
}); });
})); }
));
}); });

View File

@ -33,7 +33,7 @@ describe('direct editing - touch integration', function() {
it('should edit labels via double tap (manual test)', function(done) { it('should edit labels via double tap (manual test)', function(done) {
var xml = require('../../../fixtures/bpmn/features/label-editing/labels.bpmn'); var xml = require('./LabelEditing.bpmn');
createModeler(xml, done); createModeler(xml, done);
}); });

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"> <bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
<bpmn:process id="Process_1" isExecutable="false"> <bpmn:process id="Process_1" isExecutable="false">
<bpmn:startEvent id="StartEvent_1" name="Foo" /> <bpmn:startEvent id="StartEvent_1" name="Foo" />
<bpmn:startEvent id="StartEvent_2" /> <bpmn:startEvent id="StartEvent_2" />
<bpmn:task id="Task_1" /> <bpmn:task id="Task_1" />
<bpmn:textAnnotation id="TextAnnotation_1">
<bpmn:text></bpmn:text>
</bpmn:textAnnotation>
</bpmn:process> </bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -19,6 +22,9 @@
<bpmndi:BPMNShape id="Task_16agoun_di" bpmnElement="Task_1"> <bpmndi:BPMNShape id="Task_16agoun_di" bpmnElement="Task_1">
<dc:Bounds x="339" y="80" width="100" height="80" /> <dc:Bounds x="339" y="80" width="100" height="80" />
</bpmndi:BPMNShape> </bpmndi:BPMNShape>
<bpmndi:BPMNShape id="TextAnnotation_1_di" bpmnElement="TextAnnotation_1">
<dc:Bounds x="426" y="220" width="100" height="30" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane> </bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </bpmndi:BPMNDiagram>
</bpmn:definitions> </bpmn:definitions>

View File

@ -8,68 +8,79 @@ var modelingModule = require('lib/features/modeling'),
describe('features/modeling - update label', function() { describe('features/modeling - update label', function() {
var diagramXML = require('../../../fixtures/bpmn/features/modeling/update-label.bpmn'); var diagramXML = require('./UpdateLabel.bpmn');
beforeEach(bootstrapModeler(diagramXML, { beforeEach(bootstrapModeler(diagramXML, {
modules: [ coreModule, modelingModule ] modules: [
coreModule,
modelingModule
]
})); }));
it('should change name of start event', inject(function(modeling, elementRegistry, eventBus) { it('should change name of start event', inject(
function(modeling, elementRegistry, eventBus) {
// given // given
var startEvent_1 = elementRegistry.get('StartEvent_1'); var startEvent_1 = elementRegistry.get('StartEvent_1');
// when // when
modeling.updateLabel(startEvent_1, 'bar'); modeling.updateLabel(startEvent_1, 'bar');
// then // then
expect(startEvent_1.businessObject.name).to.equal('bar'); expect(startEvent_1.businessObject.name).to.equal('bar');
expect(startEvent_1.label.hidden).to.be.false; expect(startEvent_1.label.hidden).to.be.false;
})); }
));
it('should change name of start event with hidden label', inject(function(modeling, elementRegistry) { it('should change name of start event with hidden label', inject(
function(modeling, elementRegistry) {
// given // given
var startEvent_2 = elementRegistry.get('StartEvent_2'); var startEvent_2 = elementRegistry.get('StartEvent_2');
// when // when
modeling.updateLabel(startEvent_2, 'bar'); modeling.updateLabel(startEvent_2, 'bar');
// then // then
expect(startEvent_2.businessObject.name).to.equal('bar'); expect(startEvent_2.businessObject.name).to.equal('bar');
expect(startEvent_2.label.hidden).to.be.false; expect(startEvent_2.label.hidden).to.be.false;
})); }
));
it('should hide label when setting empty string', inject(function(modeling, elementRegistry) { it('should hide label when setting empty string', inject(
function(modeling, elementRegistry) {
// given // given
var startEvent_1 = elementRegistry.get('StartEvent_1'); var startEvent_1 = elementRegistry.get('StartEvent_1');
// when // when
modeling.updateLabel(startEvent_1, ''); modeling.updateLabel(startEvent_1, '');
// then // then
expect(startEvent_1.businessObject.name).to.equal(''); expect(startEvent_1.businessObject.name).to.equal('');
expect(startEvent_1.label.hidden).to.be.true; expect(startEvent_1.label.hidden).to.be.true;
})); }
));
it('should change name of start event when editing label', inject(function(modeling, elementRegistry) { it('should change name of start event when editing label', inject(
function(modeling, elementRegistry) {
// given // given
var startEvent_1 = elementRegistry.get('StartEvent_1'); var startEvent_1 = elementRegistry.get('StartEvent_1');
var startEvent_1_label = elementRegistry.get('StartEvent_1_label'); var startEvent_1_label = elementRegistry.get('StartEvent_1_label');
// when // when
modeling.updateLabel(startEvent_1_label, 'bar'); modeling.updateLabel(startEvent_1_label, 'bar');
// then // then
expect(startEvent_1.businessObject.name).to.equal('bar'); expect(startEvent_1.businessObject.name).to.equal('bar');
expect(startEvent_1.label.hidden).to.be.false; expect(startEvent_1.label.hidden).to.be.false;
})); }
));
it('should change name of task', inject(function(modeling, elementRegistry) { it('should change name of task', inject(function(modeling, elementRegistry) {
@ -86,8 +97,26 @@ describe('features/modeling - update label', function() {
})); }));
it('should propertly fire events.changed after event name change', it('should change text annotation text and bounds', inject(
inject(function(modeling, elementRegistry, eventBus) { function(modeling, elementRegistry) {
// given
var element = elementRegistry.get('TextAnnotation_1');
var newBounds = { x: 100, y: 100, width: 100, height: 30 };
// when
modeling.updateLabel(element, 'bar', newBounds);
// then
expect(element.businessObject.text).to.equal('bar');
expect(element).to.have.bounds(newBounds);
}
));
it('should propertly fire events.changed after event name change', inject(
function(modeling, elementRegistry, eventBus) {
// given // given
var startEvent_1 = elementRegistry.get('StartEvent_1'); var startEvent_1 = elementRegistry.get('StartEvent_1');
@ -103,12 +132,12 @@ describe('features/modeling - update label', function() {
// then // then
expect(changedEvent.elements).to.include(startEvent_1); expect(changedEvent.elements).to.include(startEvent_1);
}) }
); ));
it('should propertly fire events.changed after event label change', it('should propertly fire events.changed after event label change', inject(
inject(function(modeling, elementRegistry, eventBus) { function(modeling, elementRegistry, eventBus) {
// given // given
var startEvent_1 = elementRegistry.get('StartEvent_1'); var startEvent_1 = elementRegistry.get('StartEvent_1');
@ -125,7 +154,7 @@ describe('features/modeling - update label', function() {
// then // then
expect(changedEvent.elements).to.include(startEvent_1); expect(changedEvent.elements).to.include(startEvent_1);
}) }
); ));
}); });