feat(popup-menu): add toggle buttons for loop and MI markers

Closes #275
This commit is contained in:
pedesen 2015-06-12 15:46:28 +02:00 committed by Ricardo Matias
parent 6da0d166e3
commit af5354e337
2 changed files with 424 additions and 7 deletions

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var forEach = require('lodash/collection/forEach'), var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter'); filter = require('lodash/collection/filter');
var REPLACE_OPTIONS = require ('./ReplaceOptions'); var REPLACE_OPTIONS = require ('./ReplaceOptions');
@ -11,6 +11,9 @@ var startEventReplace = REPLACE_OPTIONS.START_EVENT,
gatewayReplace = REPLACE_OPTIONS.GATEWAY, gatewayReplace = REPLACE_OPTIONS.GATEWAY,
taskReplace = REPLACE_OPTIONS.TASK; taskReplace = REPLACE_OPTIONS.TASK;
var is = require('../../util/ModelUtil').is,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject;
/** /**
* A replace menu provider that gives users the controls to choose * A replace menu provider that gives users the controls to choose
@ -21,7 +24,7 @@ var startEventReplace = REPLACE_OPTIONS.START_EVENT,
* @param {PopupMenu} popupMenu * @param {PopupMenu} popupMenu
* @param {Replace} replace * @param {Replace} replace
*/ */
function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection) { function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection, modeling) {
/** /**
* Prepares a new business object for the replacement element * Prepares a new business object for the replacement element
@ -70,7 +73,6 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection) {
// TODO: copy other elligable properties from old business object // TODO: copy other elligable properties from old business object
businessObject.name = oldBusinessObject.name; businessObject.name = oldBusinessObject.name;
businessObject.loopCharacteristics = oldBusinessObject.loopCharacteristics; businessObject.loopCharacteristics = oldBusinessObject.loopCharacteristics;
newElement = replace.replaceElement(element, newElement); newElement = replace.replaceElement(element, newElement);
@ -80,6 +82,92 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection) {
return newElement; return newElement;
} }
var toggleEntries,
toggledElement;
function toggleMi(event, entry) {
var loopCharacteristics;
if (entry.active) {
popupMenu.update(entry, { active: false });
modeling.updateProperties(toggledElement, { loopCharacteristics: undefined });
return;
}
forEach(toggleEntries, function(action) {
var options = action.options;
if (entry.id === action.id) {
popupMenu.update(action.id, { active: true });
loopCharacteristics = moddle.create(options.loopCharacteristics);
if (options.isSequential) {
loopCharacteristics.isSequential = options.isSequential;
}
return;
}
popupMenu.update(action.id, { active: false });
});
modeling.updateProperties(toggledElement, { loopCharacteristics: loopCharacteristics });
}
function getToggleOptions(element) {
var businessObject = getBusinessObject(element),
loopCharacteristics = businessObject.loopCharacteristics;
var isSequential,
isLoop,
isParallel;
if (loopCharacteristics) {
isSequential = loopCharacteristics.isSequential;
isLoop = loopCharacteristics.isSequential === undefined;
isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
}
toggledElement = element;
toggleEntries = [
{
id: 'toggle-parallel-mi',
className: 'icon-parallel-mi-marker',
active: isParallel,
action: toggleMi,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: false
}
},
{
id: 'toggle-sequential-mi',
className: 'icon-sequential-mi-marker',
active: isSequential,
action: toggleMi,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: true
}
},
{
id: 'toggle-loop',
className: 'icon-loop-marker',
active: isLoop,
action: toggleMi,
options: {
loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
}
}
];
return toggleEntries;
}
function getReplaceOptions(element) { function getReplaceOptions(element) {
@ -159,12 +247,18 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection) {
// API // API
this.openChooser = function(position, element) { this.openChooser = function(position, element) {
var entries = this.getReplaceOptions(element); var entries = this.getReplaceOptions(element),
headerEntries;
if (is(element, 'bpmn:Activity')) {
headerEntries = this.getToggleOptions(element);
}
popupMenu.open( popupMenu.open(
{ {
className: 'replace-menu', className: 'replace-menu',
position: position, position: position,
headerEntries: headerEntries,
entries: entries entries: entries
} }
); );
@ -172,9 +266,11 @@ function BpmnReplace(bpmnFactory, moddle, popupMenu, replace, selection) {
this.getReplaceOptions = getReplaceOptions; this.getReplaceOptions = getReplaceOptions;
this.getToggleOptions = getToggleOptions;
this.replaceElement = replaceElement; this.replaceElement = replaceElement;
} }
BpmnReplace.$inject = [ 'bpmnFactory', 'moddle', 'popupMenu', 'replace', 'selection' ]; BpmnReplace.$inject = [ 'bpmnFactory', 'moddle', 'popupMenu', 'replace', 'selection', 'modeling' ];
module.exports = BpmnReplace; module.exports = BpmnReplace;

View File

@ -8,6 +8,7 @@ var TestHelper = require('../../../TestHelper'),
modelingModule = require('../../../../lib/features/modeling'), modelingModule = require('../../../../lib/features/modeling'),
replaceModule = require('../../../../lib/features/replace'), replaceModule = require('../../../../lib/features/replace'),
domQuery = require('min-dom/lib/query'), domQuery = require('min-dom/lib/query'),
domClasses = require('min-dom/lib/classes'),
is = require('../../../../lib/util/ModelUtil').is; is = require('../../../../lib/util/ModelUtil').is;
function queryEntry(popupMenu, id) { function queryEntry(popupMenu, id) {
@ -26,9 +27,328 @@ describe('features/popup-menu', function() {
beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('active attribute', function(){
it('should be true for parallel marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(loopCharacteristics.isSequential).not.toBe(undefined);
expect(loopCharacteristics.isSequential).toBe(false);
expect(popupMenu._getEntry('toggle-parallel-mi').active).toBe(true);
}));
it('should be true for sequential marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(loopCharacteristics.isSequential).toBe(true);
expect(popupMenu._getEntry('toggle-sequential-mi').active).toBe(true);
}));
it('should be true for loop marker', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask'),
loopCharacteristics = task.businessObject.loopCharacteristics;
// when
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
// then
expect(is(loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(false);
expect(loopCharacteristics.isSequential).toBe(undefined);
expect(popupMenu._getEntry('toggle-loop').active).toBe(true);
}));
});
describe('parallel toggle button', function(){
it('should toggle parallel marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
it('should toggle parallel marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(false);
expect(domClasses(parallelEntry).has('active')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-parallel-mi');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
});
describe('sequential toggle button', function(){
it('should toggle sequential marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(task.businessObject.loopCharacteristics).toBe(undefined);
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should toggle sequential marker on', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(task.businessObject.loopCharacteristics.isSequential).toBe(true);
expect(domClasses(sequentialEntry).has('active')).toBe(true);
}));
it('should set loop button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-sequential-mi');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
describe('loop toggle button', function(){
it('should toggle loop marker off', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('LoopTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(undefined);
expect(domClasses(loopEntry).has('active')).toBe(false);
}));
it('should toggle loop marker on', inject(function(popupMenu, bpmnReplace, elementRegistry){
// given
var task = elementRegistry.get('Task');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop'),
evt = { target: entry, preventDefault: function(){} };
// when
popupMenu.trigger(evt);
var loopEntry = queryEntry(popupMenu, 'toggle-loop');
// then
expect(is(task.businessObject.loopCharacteristics, 'bpmn:StandardLoopCharacteristics')).toBe(true);
expect(domClasses(loopEntry).has('active')).toBe(true);
}));
it('should set sequential button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var sequentialEntry = queryEntry(popupMenu, 'toggle-sequential-mi');
// then
expect(domClasses(sequentialEntry).has('active')).toBe(false);
}));
it('should set parallel button inactive', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('ParallelTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'toggle-loop');
// when
popupMenu.trigger({ target: entry, preventDefault: function(){} });
var parallelEntry = queryEntry(popupMenu, 'toggle-parallel-mi');
// then
expect(domClasses(parallelEntry).has('active')).toBe(false);
}));
});
describe('replacing a task', function() { describe('replacing a task', function() {
it('should retain the loop characteristics', inject(function(popupMenu, bpmnReplace, elementRegistry) {
// given
var task = elementRegistry.get('SequentialTask');
bpmnReplace.openChooser({ x: task.x + 100, y: task.y + 100 }, task);
var entry = queryEntry(popupMenu, 'replace-with-send-task');
// when
// replacing the task with a send task
popupMenu.trigger({ target: entry, preventDefault: function(){} });
// then
// get the send task from the registry
var sendTask = elementRegistry.filter(function(element, gfx) { return element.type === 'bpmn:SendTask';})[0];
expect(sendTask.businessObject.loopCharacteristics).toBeDefined();
expect(is(sendTask.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(sendTask.businessObject.loopCharacteristics.isSequential).toBe(true);
}));
it('should retain the loop characteristics for call activites', it('should retain the loop characteristics for call activites',
inject(function(popupMenu, bpmnReplace, elementRegistry) { inject(function(popupMenu, bpmnReplace, elementRegistry) {
@ -52,6 +372,7 @@ describe('features/popup-menu', function() {
expect(callActivity.businessObject.loopCharacteristics).toBeDefined(); expect(callActivity.businessObject.loopCharacteristics).toBeDefined();
expect(is(callActivity.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true); expect(is(callActivity.businessObject.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')).toBe(true);
expect(callActivity.businessObject.loopCharacteristics.isSequential).toBe(true); expect(callActivity.businessObject.loopCharacteristics.isSequential).toBe(true);
})); }));
}); });