Squashed 'bpmn-js-spiffworkflow/' changes from a547888ef..c4843c17b
c4843c17b Merge pull request #26 from sartography/feature/inherited-data-objects 9e2a8f384 minor tweak for adding data objects to lanes/participants. ada919e59 add a few tests on data object visibility 627e771d4 allow subprocesses to inherit data objects 887f318f7 Minor cleanup of display in bpmn-js git-subtree-dir: bpmn-js-spiffworkflow git-subtree-split: c4843c17b869d1c730494dc10ddb31d761e2ac40
This commit is contained in:
parent
3add069267
commit
1be006811b
|
@ -3,7 +3,7 @@ import {
|
||||||
BpmnPropertiesPanelModule,
|
BpmnPropertiesPanelModule,
|
||||||
BpmnPropertiesProviderModule,
|
BpmnPropertiesProviderModule,
|
||||||
} from 'bpmn-js-properties-panel';
|
} from 'bpmn-js-properties-panel';
|
||||||
import diagramXML from '../test/spec/bpmn/basic_message.bpmn';
|
import diagramXML from '../test/spec/bpmn/empty_diagram.bpmn';
|
||||||
import spiffworkflow from './spiffworkflow';
|
import spiffworkflow from './spiffworkflow';
|
||||||
import setupFileOperations from './fileOperations';
|
import setupFileOperations from './fileOperations';
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* @param container
|
* @param container
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export function findDataObjects(parent, dataObjects) {
|
||||||
export function findDataObjects(parent) {
|
if (typeof(dataObjects) === 'undefined')
|
||||||
let dataObjects = [];
|
dataObjects = [];
|
||||||
let process;
|
let process;
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -15,15 +15,12 @@ export function findDataObjects(parent) {
|
||||||
process = parent.processRef;
|
process = parent.processRef;
|
||||||
} else {
|
} else {
|
||||||
process = parent;
|
process = parent;
|
||||||
|
if (process.$type === 'bpmn:SubProcess')
|
||||||
|
findDataObjects(process.$parent, dataObjects);
|
||||||
}
|
}
|
||||||
if (!process.flowElements) {
|
if (typeof(process.flowElements) !== 'undefined') {
|
||||||
return [];
|
|
||||||
}
|
|
||||||
for (const element of process.flowElements) {
|
for (const element of process.flowElements) {
|
||||||
if (
|
if (element.$type === 'bpmn:DataObject')
|
||||||
element.$type === 'bpmn:DataObject' &&
|
|
||||||
dataObjects.indexOf(element) < 0
|
|
||||||
) {
|
|
||||||
dataObjects.push(element);
|
dataObjects.push(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,16 +35,26 @@ export function findDataObject(process, id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findDataReferenceShapes(processShape, id) {
|
export function findDataObjectReferences(children, dataObjectId) {
|
||||||
let refs = [];
|
return children.flatMap((child) => {
|
||||||
for (const shape of processShape.children) {
|
if (child.$type == 'bpmn:DataObjectReference' && child.dataObjectRef.id == dataObjectId)
|
||||||
if (shape.type === 'bpmn:DataObjectReference') {
|
return [child];
|
||||||
if (shape.businessObject.dataObjectRef && shape.businessObject.dataObjectRef.id === id) {
|
else if (child.$type == 'bpmn:SubProcess')
|
||||||
refs.push(shape);
|
return findDataObjectReferences(child.get('flowElements'), dataObjectId);
|
||||||
|
else
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
export function findDataObjectReferenceShapes(children, dataObjectId) {
|
||||||
return refs;
|
return children.flatMap((child) => {
|
||||||
|
if (child.type == 'bpmn:DataObjectReference' && child.businessObject.dataObjectRef.id == dataObjectId)
|
||||||
|
return [child];
|
||||||
|
else if (child.type == 'bpmn:SubProcess')
|
||||||
|
return findDataObjectReferenceShapes(child.children, dataObjectId);
|
||||||
|
else
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function idToHumanReadableName(id) {
|
export function idToHumanReadableName(id) {
|
||||||
|
|
|
@ -3,11 +3,12 @@ import { getDi, is } from 'bpmn-js/lib/util/ModelUtil';
|
||||||
import { remove as collectionRemove } from 'diagram-js/lib/util/Collections';
|
import { remove as collectionRemove } from 'diagram-js/lib/util/Collections';
|
||||||
import {
|
import {
|
||||||
findDataObjects,
|
findDataObjects,
|
||||||
findDataReferenceShapes,
|
findDataObjectReferences,
|
||||||
idToHumanReadableName,
|
idToHumanReadableName,
|
||||||
} from './DataObjectHelpers';
|
} from './DataObjectHelpers';
|
||||||
|
|
||||||
const HIGH_PRIORITY = 1500;
|
const HIGH_PRIORITY = 1500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events
|
* This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events
|
||||||
* from Diagram.js and updates the underlying BPMN model accordingly.
|
* from Diagram.js and updates the underlying BPMN model accordingly.
|
||||||
|
@ -20,9 +21,40 @@ const HIGH_PRIORITY = 1500;
|
||||||
* 4) Don't allow someone to move a DataObjectReference from one process to another process.
|
* 4) Don't allow someone to move a DataObjectReference from one process to another process.
|
||||||
*/
|
*/
|
||||||
export default class DataObjectInterceptor extends CommandInterceptor {
|
export default class DataObjectInterceptor extends CommandInterceptor {
|
||||||
constructor(eventBus, bpmnFactory, commandStack) {
|
|
||||||
|
constructor(eventBus, bpmnFactory, commandStack, bpmnUpdater) {
|
||||||
super(eventBus);
|
super(eventBus);
|
||||||
|
|
||||||
|
/* The default behavior is to move the data object into whatever object the reference is being created in.
|
||||||
|
* If a data object already has a parent, don't change it.
|
||||||
|
*/
|
||||||
|
bpmnUpdater.updateSemanticParent = (businessObject, parentBusinessObject) => {
|
||||||
|
// Special case for participant - which is a valid place to drop a data object, but it needs to be added
|
||||||
|
// to the particpant's Process (which isn't directly accessible in BPMN.io
|
||||||
|
let realParent = parentBusinessObject;
|
||||||
|
if (is(realParent, 'bpmn:Participant')) {
|
||||||
|
realParent = realParent.processRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is(businessObject, 'bpmn:DataObjectReference')) {
|
||||||
|
// For data object references, always update the flowElements when a parent is provided
|
||||||
|
// The parent could be null if it's being deleted, and I could probably handle that here instead of
|
||||||
|
// when the shape is deleted, but not interested in refactoring at the moment.
|
||||||
|
if (realParent != null) {
|
||||||
|
const flowElements = realParent.get('flowElements');
|
||||||
|
flowElements.push(businessObject);
|
||||||
|
}
|
||||||
|
} else if (is(businessObject, 'bpmn:DataObject')) {
|
||||||
|
// For data objects, only update the flowElements for new data objects, and set the parent so it doesn't get moved.
|
||||||
|
if (typeof(businessObject.$parent) === 'undefined') {
|
||||||
|
const flowElements = realParent.get('flowElements');
|
||||||
|
flowElements.push(businessObject);
|
||||||
|
businessObject.$parent = realParent;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
bpmnUpdater.__proto__.updateSemanticParent.call(this, businessObject, parentBusinessObject);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For DataObjectReferences only ...
|
* For DataObjectReferences only ...
|
||||||
* Prevent this from calling the CreateDataObjectBehavior in BPMN-js, as it will
|
* Prevent this from calling the CreateDataObjectBehavior in BPMN-js, as it will
|
||||||
|
@ -52,9 +84,9 @@ export default class DataObjectInterceptor extends CommandInterceptor {
|
||||||
} else {
|
} else {
|
||||||
dataObject = bpmnFactory.create('bpmn:DataObject');
|
dataObject = bpmnFactory.create('bpmn:DataObject');
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the reference to the DataObject
|
// set the reference to the DataObject
|
||||||
shape.businessObject.dataObjectRef = dataObject;
|
shape.businessObject.dataObjectRef = dataObject;
|
||||||
|
shape.businessObject.$parent = process;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -84,38 +116,19 @@ export default class DataObjectInterceptor extends CommandInterceptor {
|
||||||
*/
|
*/
|
||||||
this.executed(['shape.delete'], HIGH_PRIORITY, function (event) {
|
this.executed(['shape.delete'], HIGH_PRIORITY, function (event) {
|
||||||
const { context } = event;
|
const { context } = event;
|
||||||
const { shape, oldParent } = context;
|
const { shape } = context;
|
||||||
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
||||||
const references = findDataReferenceShapes(
|
const dataObject = shape.businessObject.dataObjectRef;
|
||||||
oldParent,
|
let flowElements = shape.businessObject.$parent.get('flowElements');
|
||||||
shape.businessObject.dataObjectRef.id
|
collectionRemove(flowElements, shape.businessObject);
|
||||||
);
|
let references = findDataObjectReferences(flowElements, dataObject.id);
|
||||||
if (references.length === 0) {
|
if (references.length === 0) {
|
||||||
return; // Use the default bahavior and delete the data object.
|
let flowElements = dataObject.$parent.get('flowElements');
|
||||||
|
collectionRemove(flowElements, dataObject);
|
||||||
}
|
}
|
||||||
// Remove the business Object
|
|
||||||
let containment = '';
|
|
||||||
const { businessObject } = shape;
|
|
||||||
if (is(businessObject, 'bpmn:DataOutputAssociation')) {
|
|
||||||
containment = 'dataOutputAssociations';
|
|
||||||
}
|
|
||||||
if (is(businessObject, 'bpmn:DataInputAssociation')) {
|
|
||||||
containment = 'dataInputAssociations';
|
|
||||||
}
|
|
||||||
const children = businessObject.$parent.get(containment);
|
|
||||||
collectionRemove(children, businessObject);
|
|
||||||
|
|
||||||
// Remove the visible element.
|
|
||||||
const di = getDi(shape);
|
|
||||||
const planeElements = di.$parent.get('planeElement');
|
|
||||||
collectionRemove(planeElements, di);
|
|
||||||
di.$parent = null;
|
|
||||||
|
|
||||||
// Stop the propogation.
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataObjectInterceptor.$inject = ['eventBus', 'bpmnFactory', 'commandStack'];
|
DataObjectInterceptor.$inject = ['eventBus', 'bpmnFactory', 'commandStack', 'bpmnUpdater'];
|
||||||
|
|
|
@ -5,7 +5,11 @@ import {
|
||||||
} from '@bpmn-io/properties-panel';
|
} from '@bpmn-io/properties-panel';
|
||||||
import { without } from 'min-dash';
|
import { without } from 'min-dash';
|
||||||
import { is } from 'bpmn-js/lib/util/ModelUtil';
|
import { is } from 'bpmn-js/lib/util/ModelUtil';
|
||||||
import {findDataObjects, findDataReferenceShapes, idToHumanReadableName} from '../DataObjectHelpers';
|
import {
|
||||||
|
findDataObjects,
|
||||||
|
findDataObjectReferenceShapes,
|
||||||
|
idToHumanReadableName,
|
||||||
|
} from '../DataObjectHelpers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of data objects, and allows you to add / remove data objects, and change their ids.
|
* Provides a list of data objects, and allows you to add / remove data objects, and change their ids.
|
||||||
|
@ -20,7 +24,7 @@ export function DataObjectArray(props) {
|
||||||
let process;
|
let process;
|
||||||
|
|
||||||
// This element might be a process, or something that will reference a process.
|
// This element might be a process, or something that will reference a process.
|
||||||
if (is(element.businessObject, 'bpmn:Process')) {
|
if (is(element.businessObject, 'bpmn:Process') || is(element.businessObject, 'bpmn:SubProcess')) {
|
||||||
process = element.businessObject;
|
process = element.businessObject;
|
||||||
} else if (element.businessObject.processRef) {
|
} else if (element.businessObject.processRef) {
|
||||||
process = element.businessObject.processRef;
|
process = element.businessObject.processRef;
|
||||||
|
@ -53,6 +57,7 @@ export function DataObjectArray(props) {
|
||||||
const newDataObject = moddle.create('bpmn:DataObject');
|
const newDataObject = moddle.create('bpmn:DataObject');
|
||||||
const newElements = process.get('flowElements');
|
const newElements = process.get('flowElements');
|
||||||
newDataObject.id = moddle.ids.nextPrefixed('DataObject_');
|
newDataObject.id = moddle.ids.nextPrefixed('DataObject_');
|
||||||
|
newDataObject.$parent = process;
|
||||||
newElements.push(newDataObject);
|
newElements.push(newDataObject);
|
||||||
commandStack.execute('element.updateModdleProperties', {
|
commandStack.execute('element.updateModdleProperties', {
|
||||||
element,
|
element,
|
||||||
|
@ -79,7 +84,7 @@ function removeFactory(props) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// When a data object is removed, remove all references as well.
|
// When a data object is removed, remove all references as well.
|
||||||
const references = findDataReferenceShapes(element, dataObject.id);
|
const references = findDataObjectReferenceShapes(element.children, dataObject.id);
|
||||||
for (const ref of references) {
|
for (const ref of references) {
|
||||||
commandStack.execute('shape.delete', { shape: ref });
|
commandStack.execute('shape.delete', { shape: ref });
|
||||||
}
|
}
|
||||||
|
@ -116,7 +121,7 @@ function DataObjectTextField(props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also update the label of all the references
|
// Also update the label of all the references
|
||||||
const references = findDataReferenceShapes(element, dataObject.id);
|
const references = findDataObjectReferenceShapes(element.children, dataObject.id);
|
||||||
for (const ref of references) {
|
for (const ref of references) {
|
||||||
commandStack.execute('element.updateProperties', {
|
commandStack.execute('element.updateProperties', {
|
||||||
element: ref,
|
element: ref,
|
||||||
|
|
|
@ -20,7 +20,8 @@ export default function DataObjectPropertiesProvider(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
isAny(element, ['bpmn:Process', 'bpmn:SubProcess', 'bpmn:Participant'])
|
isAny(element, ['bpmn:Process', 'bpmn:Participant']) ||
|
||||||
|
(is(element, 'bpmn:SubProcess') && !element.collapsed)
|
||||||
) {
|
) {
|
||||||
groups.push(
|
groups.push(
|
||||||
createDataObjectEditor(
|
createDataObjectEditor(
|
||||||
|
|
|
@ -37,7 +37,7 @@ export function CorrelationPropertiesArray(props) {
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
label: correlationPropertyModdleElement.id,
|
label: correlationPropertyModdleElement.name,
|
||||||
entries,
|
entries,
|
||||||
autoFocusEntry: id,
|
autoFocusEntry: id,
|
||||||
remove: removeFactory({
|
remove: removeFactory({
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function MessageCorrelationPropertiesArray(props) {
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
label: correlationPropertyModdleElement.id,
|
label: correlationPropertyModdleElement.name,
|
||||||
entries,
|
entries,
|
||||||
autoFocusEntry: id,
|
autoFocusEntry: id,
|
||||||
remove: removeFactory({
|
remove: removeFactory({
|
||||||
|
|
|
@ -4,7 +4,11 @@ import { BpmnPropertiesPanelModule, BpmnPropertiesProviderModule } from 'bpmn-js
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
} from 'bpmn-js/test/helper';
|
} from 'bpmn-js/test/helper';
|
||||||
import {findDataObjects, idToHumanReadableName} from '../../app/spiffworkflow/DataObject/DataObjectHelpers';
|
import {
|
||||||
|
findDataObjects,
|
||||||
|
findDataObjectReferenceShapes,
|
||||||
|
idToHumanReadableName,
|
||||||
|
} from '../../app/spiffworkflow/DataObject/DataObjectHelpers';
|
||||||
|
|
||||||
describe('DataObject Interceptor', function() {
|
describe('DataObject Interceptor', function() {
|
||||||
|
|
||||||
|
@ -113,4 +117,52 @@ describe('DataObject Interceptor', function() {
|
||||||
expect(dataObjects.length).to.equal(1);
|
expect(dataObjects.length).to.equal(1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('Data objects in a process should be visible in a subprocess', inject(function(canvas, modeling, elementRegistry) {
|
||||||
|
|
||||||
|
let subProcessShape = elementRegistry.get('my_subprocess');
|
||||||
|
let subProcess = subProcessShape.businessObject;
|
||||||
|
let dataObjects = findDataObjects(subProcess);
|
||||||
|
expect(dataObjects.length).to.equal(0);
|
||||||
|
|
||||||
|
let rootShape = canvas.getRootElement();
|
||||||
|
const dataObjectRefShape = modeling.createShape({ type: 'bpmn:DataObjectReference' },
|
||||||
|
{ x: 220, y: 220 }, rootShape);
|
||||||
|
|
||||||
|
dataObjects = findDataObjects(subProcess);
|
||||||
|
expect(dataObjects.length).to.equal(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Data objects in a subprocess should not be visible in a process', inject(function(canvas, modeling, elementRegistry) {
|
||||||
|
|
||||||
|
let subProcessShape = elementRegistry.get('my_subprocess');
|
||||||
|
let subProcess = subProcessShape.businessObject;
|
||||||
|
const dataObjectRefShape = modeling.createShape({ type: 'bpmn:DataObjectReference' },
|
||||||
|
{ x: 220, y: 220 }, subProcessShape);
|
||||||
|
|
||||||
|
let dataObjects = findDataObjects(subProcess);
|
||||||
|
expect(dataObjects.length).to.equal(1);
|
||||||
|
|
||||||
|
let rootShape = canvas.getRootElement();
|
||||||
|
dataObjects = findDataObjects(rootShape);
|
||||||
|
expect(dataObjects.length).to.equal(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('References inside subprocesses should be visible in a process', inject(function(canvas, modeling, elementRegistry) {
|
||||||
|
|
||||||
|
let rootShape = canvas.getRootElement();
|
||||||
|
const refOne = modeling.createShape({ type: 'bpmn:DataObjectReference' },
|
||||||
|
{ x: 220, y: 220 }, rootShape);
|
||||||
|
|
||||||
|
let subProcessShape = elementRegistry.get('my_subprocess');
|
||||||
|
let subProcess = subProcessShape.businessObject;
|
||||||
|
const refTwo = modeling.createShape({ type: 'bpmn:DataObjectReference' },
|
||||||
|
{ x: 320, y: 220 }, subProcessShape);
|
||||||
|
|
||||||
|
let dataObjects = findDataObjects(subProcess);
|
||||||
|
expect(dataObjects.length).to.equal(1);
|
||||||
|
let references = findDataObjectReferenceShapes(rootShape.children, dataObjects[0].id);
|
||||||
|
expect(references.length).to.equal(2);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue