Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(create-append-anything): create with templates #246

Merged
merged 1 commit into from
Feb 15, 2023
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { assign } from 'min-dash';

/**
* A popup menu provider that allows to create elements with
* element templates.
*/
export default function ElementTemplatesCreateProvider(
popupMenu, translate, elementTemplates,
mouse, create) {

this._popupMenu = popupMenu;
this._translate = translate;
this._elementTemplates = elementTemplates;
this._mouse = mouse;
this._create = create;

this.register();
}

ElementTemplatesCreateProvider.$inject = [
'popupMenu',
'translate',
'elementTemplates',
'mouse',
'create'
];

/**
* Register create menu provider in the popup menu
*/
ElementTemplatesCreateProvider.prototype.register = function() {
this._popupMenu.registerProvider('bpmn-create', this);
};

/**
* Adds the element templates to the create menu.
* @param {djs.model.Base} element
*
* @returns {Object}
*/
ElementTemplatesCreateProvider.prototype.getPopupMenuEntries = function(element) {
return (entries) => {

// add template entries
assign(entries, this.getTemplateEntries(element));

return entries;
};
};

/**
* Get all element templates.
*
* @param {djs.model.Base} element
*
* @return {Array<Object>} a list of element templates as menu entries
*/
ElementTemplatesCreateProvider.prototype.getTemplateEntries = function() {

const templates = this._elementTemplates.getLatest();
const templateEntries = {};

templates.map(template => {

const {
icon = {},
category,
} = template;

const entryId = `create.template-${template.id}`;

const defaultGroup = {
id: 'templates',
name: this._translate('Templates')
};

templateEntries[entryId] = {
label: template.name,
description: template.description,
documentationRef: template.documentationRef,
imageUrl: icon.contents,
group: category || defaultGroup,
action: {
click: this._getEntryAction(template),
dragstart: this._getEntryAction(template)
}
};
});

return templateEntries;
};


ElementTemplatesCreateProvider.prototype._getEntryAction = function(template) {
const create = this._create;
const popupMenu = this._popupMenu;
const elementTemplates = this._elementTemplates;
const mouse = this._mouse;

return (event) => {

popupMenu.close();

// create the new element
let newElement = elementTemplates.createElement(template);

// use last mouse event if triggered via keyboard
if (event instanceof KeyboardEvent) {
event = mouse.getLastMoveEvent();
}

return create.start(event, newElement);
};
};
9 changes: 7 additions & 2 deletions lib/camunda-cloud/features/create-append-anything/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import ElementTemplatesAppendProvider from './ElementTemplatesAppendProvider';
import ElementTemplatesCreateProvider from './ElementTemplatesCreateProvider';

export default {
__init__: [ 'elementTemplatesAppendProvider' ],
elementTemplatesAppendProvider: [ 'type', ElementTemplatesAppendProvider ]
__init__: [
'elementTemplatesAppendProvider',
'elementTemplatesCreateProvider'
],
elementTemplatesAppendProvider: [ 'type', ElementTemplatesAppendProvider ],
elementTemplatesCreateProvider: [ 'type', ElementTemplatesCreateProvider ]
};
1 change: 1 addition & 0 deletions test/TestHelper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from 'bpmn-js/test/helper';

export {
createCanvasEvent,
createEvent
} from 'bpmn-js/test/util/MockEvents';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?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:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0su05ks" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.8.0-rc.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.1.0">
<bpmn:process id="Process_1uc9zgy" isExecutable="true" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1uc9zgy" />
</bpmndi:BPMNDiagram>
</bpmn:definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import {
inject,
getBpmnJS,
bootstrapCamundaCloudModeler,
createCanvasEvent,
createEvent as globalEvent
} from 'test/TestHelper';

import {
query as domQuery
} from 'min-dom';

import diagramXML from './ElementTemplatesCreateProvider.bpmn';
import templates from './ElementTemplatesAppendProvider.json';

import {
getBusinessObject,
is
} from 'bpmn-js/lib/util/ModelUtil';


describe('<ElementTemplatesCreateProviderSpec>', function() {

beforeEach(bootstrapCamundaCloudModeler(diagramXML));

beforeEach(inject(function(elementTemplates) {
elementTemplates.set(templates);
}));


describe('display', function() {

it('should display template options', inject(function(canvas) {

// given
const rootElement = canvas.getRootElement();

// when
openPopup(rootElement);

// then
const entries = Object.keys(getEntries());
const templateEntries = entries.filter((entry) => entry.startsWith('create.template-'));

expect(templateEntries.length).to.eql(templates.length);
}));

});


describe('create', function() {

it('should create template', inject(function(elementRegistry, selection) {

// given
const template = templates[0];

// when
triggerEntry(`create.template-${template.id}`);

// then
expectElementWithTemplate(elementRegistry, 'bpmn:Task', template, true);
expectSelected(selection, 'bpmn:Task');
}));


it('should undo', inject(function(elementRegistry, commandStack, selection) {

// given
const template = templates[0];

// when
triggerEntry(`create.template-${template.id}`);

// then
expectElementWithTemplate(elementRegistry, 'bpmn:Task', template);
expectSelected(selection, 'bpmn:Task');

// when
commandStack.undo();

// then
expectElementWithTemplate(elementRegistry, 'bpmn:Task', template, false);
expectSelected(selection, 'bpmn:Task', false);
}));


it('should redo', inject(function(elementRegistry, commandStack, selection) {

// given
const template = templates[0];

// when
triggerEntry(`create.template-${template.id}`);
commandStack.undo();

// then
expectElementWithTemplate(elementRegistry, 'bpmn:Task', template, false);
expectSelected(selection, 'bpmn:Task', false);

// when
commandStack.redo();

// then
expectElementWithTemplate(elementRegistry, 'bpmn:Task', template);
}));

});

});


// helpers ////////////

function openPopup(element, offset) {
offset = offset || 100;

getBpmnJS().invoke(function(popupMenu) {
popupMenu.open(element, 'bpmn-create', {
x: element.x, y: element.y
});

});
}

function queryEntry(id) {
var container = getMenuContainer();

return domQuery('.djs-popup [data-id="' + id + '"]', container);
}

function getMenuContainer() {
const popup = getBpmnJS().get('popupMenu');
return popup._current.container;
}

function triggerAction(id) {
const entry = queryEntry(id);

if (!entry) {
throw new Error('entry "' + id + '" not found in append menu');
}

const popupMenu = getBpmnJS().get('popupMenu');

return popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
}

function getEntries() {
const popupMenu = getBpmnJS().get('popupMenu');
return popupMenu._current.entries;
}

function isTemplateApplied(element, template) {
const businessObject = getBusinessObject(element);

return businessObject.get('modelerTemplate') === template.id;
}

function expectElementWithTemplate(elementRegistry, type, template, result = true) {
const element = elementRegistry.find((element) => is(element, type));

if (!result) {
expect(element).to.not.exist;
} else {
expect(element).to.exist;
expect(isTemplateApplied(element, template)).to.be.true;
}
}

function expectSelected(selection, type, result = true) {
const selected = selection.get();

if (!result) {
expect(selected).to.have.length(0);
} else {
expect(selected).to.have.length(1);
expect(is(selected[0], type)).to.be.true;
}
}

function triggerEntry(id) {

return getBpmnJS().invoke(function(canvas, dragging) {

var rootElement = canvas.getRootElement(),
rootGfx = canvas.getGraphics(rootElement);

openPopup(rootElement);
triggerAction(id);

dragging.hover({ element: rootElement, gfx: rootGfx });
dragging.move(createCanvasEvent({ x: 200, y: 300 }));

// when
dragging.end();

});
}