diff --git a/src/component/mxgraph/config/ShapeConfigurator.ts b/src/component/mxgraph/config/ShapeConfigurator.ts index 36e063e887..4a3d2be8ee 100644 --- a/src/component/mxgraph/config/ShapeConfigurator.ts +++ b/src/component/mxgraph/config/ShapeConfigurator.ts @@ -17,7 +17,7 @@ limitations under the License. import type { mxShape } from 'mxgraph'; import { ShapeBpmnElementKind } from '../../../model/bpmn/internal'; -import { mxCellRenderer, mxConstants, mxSvgCanvas2D } from '../initializer'; +import { mxCellRenderer } from '../initializer'; import { BusinessRuleTaskShape, CallActivityShape, @@ -81,53 +81,6 @@ const registerShapes = (): void => { */ export default class ShapeConfigurator { configureShapes(): void { - this.initMxSvgCanvasPrototype(); registerShapes(); } - - private initMxSvgCanvasPrototype(): void { - // getTextCss is only used when creating foreignObject for label, so there is no impact on svg text that we use for Overlays. - // Analysis done for mxgraph@4.1.1, still apply to mxgraph@4.2.2 - mxSvgCanvas2D.prototype.getTextCss = function () { - const s = this.state; - const lh = mxConstants.ABSOLUTE_LINE_HEIGHT ? s.fontSize * mxConstants.LINE_HEIGHT + 'px' : mxConstants.LINE_HEIGHT * this.lineHeightCorrection; - let css = - 'display: inline-block; font-size: ' + - s.fontSize + - 'px; ' + - 'font-family: ' + - s.fontFamily + - '; color: ' + - s.fontColor + - '; line-height: ' + - lh + - // START Fix for issue #920 (https://github.com/process-analytics/bpmn-visualization-js/issues/920) - // This cannot be generalized for all mxgraph use cases. For instance, in an editor mode, we should be able to edit the text by clicking on it. - // Setting to 'none' prevent to capture click. - '; pointer-events: none' + - // (this.pointerEvents ? this.pointerEventsValue : 'none') + - // END OF Fix for issue #920 - '; '; - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) { - css += 'font-weight: bold; '; - } - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) { - css += 'font-style: italic; '; - } - - const deco = []; - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) { - deco.push('underline'); - } - if ((s.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH) { - deco.push('line-through'); - } - if (deco.length > 0) { - css += 'text-decoration: ' + deco.join(' ') + '; '; - } - - return css; - }; - } } diff --git a/src/component/mxgraph/shape/utils.ts b/src/component/mxgraph/shape/utils.ts index 3684ae7592..27a2a4b966 100644 --- a/src/component/mxgraph/shape/utils.ts +++ b/src/component/mxgraph/shape/utils.ts @@ -24,7 +24,10 @@ export const overrideCreateSvgCanvas = function (shape: mxShape): void { // The following is copied from the mxgraph mxShape implementation then converted to TypeScript and enriched for bpmn-visualization // It is needed for adding the custom attributes that permits identification of the BPMN elements in the DOM shape.createSvgCanvas = function () { - const canvas = new mxSvgCanvas2D(this.node, false); + // START bpmn-visualization CUSTOMIZATION + // use custom canvas implementation + const canvas = new SvgCanvas2D(this.node, false); + // END bpmn-visualization CUSTOMIZATION canvas.strokeTolerance = this.pointerEvents ? this.svgStrokeTolerance : 0; canvas.pointerEventsValue = this.svgPointerEvents; const off = this.getSvgScreenOffset(); @@ -65,3 +68,19 @@ export const overrideCreateSvgCanvas = function (shape: mxShape): void { return canvas; }; }; + +class SvgCanvas2D extends mxSvgCanvas2D { + // getTextCss is only used when creating foreignObject for label, so there is no impact on svg text that we use for Overlays. + // Analysis done for mxgraph@4.1.1, still apply to mxgraph@4.2.2 + override getTextCss(): string { + const originalPointerEvents = this.pointerEvents; + // Fix for issue https://github.com/process-analytics/bpmn-visualization-js/issues/920 + // This sets the "pointer-events" style property to "none" to avoid capturing the click. + // This cannot be generalized for all mxgraph use cases. For instance, in an editor mode, we should be able to edit the text by clicking on it. + this.pointerEvents = false; + + const textCss = super.getTextCss(); + this.pointerEvents = originalPointerEvents; + return textCss; + } +} diff --git a/test/integration/dom.container.div.test.ts b/test/integration/dom.container.div.test.ts index a2aa75e50c..22c255f943 100644 --- a/test/integration/dom.container.div.test.ts +++ b/test/integration/dom.container.div.test.ts @@ -37,18 +37,18 @@ describe.each` it('DOM should contains BPMN elements when loading simple-start-task-end.bpmn', () => { bpmnVisualization.load(readFileSync('../fixtures/bpmn/simple-start-task-end.bpmn')); - htmlElementLookup.expectStartEvent('StartEvent_1', ShapeBpmnEventDefinitionKind.NONE); - htmlElementLookup.expectTask('Activity_1'); - htmlElementLookup.expectEndEvent('EndEvent_1', ShapeBpmnEventDefinitionKind.NONE); + htmlElementLookup.expectStartEvent('StartEvent_1', ShapeBpmnEventDefinitionKind.NONE, { label: 'Start Event 1' }); + htmlElementLookup.expectTask('Activity_1', { label: 'Task 1' }); + htmlElementLookup.expectEndEvent('EndEvent_1', ShapeBpmnEventDefinitionKind.NONE, { label: 'End Event 1' }); }); it('DOM should contains BPMN elements when loading model-complete-semantic.bpmn', () => { bpmnVisualization.load(readFileSync('../fixtures/bpmn/model-complete-semantic.bpmn')); - htmlElementLookup.expectPool('participant_1_id'); - htmlElementLookup.expectLane('lane_4_1_id'); + htmlElementLookup.expectPool('participant_1_id', { label: 'Pool 1' }); + htmlElementLookup.expectLane('lane_4_1_id', { label: 'Lane with child lanes' }); - htmlElementLookup.expectStartEvent('start_event_signal_id', ShapeBpmnEventDefinitionKind.SIGNAL); - htmlElementLookup.expectIntermediateThrowEvent('intermediate_throw_event_message_id', ShapeBpmnEventDefinitionKind.MESSAGE); + htmlElementLookup.expectStartEvent('start_event_signal_id', ShapeBpmnEventDefinitionKind.SIGNAL, { label: 'Signal Start Event' }); + htmlElementLookup.expectIntermediateThrowEvent('intermediate_throw_event_message_id', ShapeBpmnEventDefinitionKind.MESSAGE, { label: 'Throw Message Intermediate Event' }); }); }); diff --git a/test/integration/helpers/html-utils.ts b/test/integration/helpers/html-utils.ts index 58434088c3..cfc23dde0d 100644 --- a/test/integration/helpers/html-utils.ts +++ b/test/integration/helpers/html-utils.ts @@ -58,8 +58,8 @@ export class HtmlElementLookup { this.expectEventType(bpmnId, 'bpmn-start-event', bpmnEventDefinition, checks); } - expectIntermediateThrowEvent(bpmnId: string, bpmnEventDefinition: ShapeBpmnEventDefinitionKind): void { - this.expectEventType(bpmnId, 'bpmn-intermediate-throw-event', bpmnEventDefinition); + expectIntermediateThrowEvent(bpmnId: string, bpmnEventDefinition: ShapeBpmnEventDefinitionKind, checks?: RequestedChecks): void { + this.expectEventType(bpmnId, 'bpmn-intermediate-throw-event', bpmnEventDefinition, checks); } expectEndEvent(bpmnId: string, bpmnEventDefinition: ShapeBpmnEventDefinitionKind, checks?: RequestedChecks): void { @@ -74,8 +74,8 @@ export class HtmlElementLookup { this.expectElement(bpmnId, expectSvgTask, ['bpmn-type-activity', 'bpmn-type-task', bpmnClass], checks); } - expectTask(bpmnId: string): void { - this.expectTaskType(bpmnId, 'bpmn-task'); + expectTask(bpmnId: string, checks?: RequestedChecks): void { + this.expectTaskType(bpmnId, 'bpmn-task', checks); } expectServiceTask(bpmnId: string, checks?: RequestedChecks): void { @@ -94,8 +94,8 @@ export class HtmlElementLookup { this.expectElement(bpmnId, expectSvgLane, ['bpmn-type-container', 'bpmn-lane'], checks); } - expectPool(bpmnId: string): void { - this.expectElement(bpmnId, expectSvgPool, ['bpmn-type-container', 'bpmn-pool']); + expectPool(bpmnId: string, checks?: RequestedChecks): void { + this.expectElement(bpmnId, expectSvgPool, ['bpmn-type-container', 'bpmn-pool'], checks); } // =========================================================================== @@ -169,6 +169,9 @@ export class HtmlElementLookup { const labelLastDivElement = this.querySelector(this.bpmnQuerySelectors.labelLastDiv(bpmnId)); expect(labelLastDivElement.innerHTML).toEqual(label); + // Validate fix for https://github.com/process-analytics/bpmn-visualization-js/issues/920 + expect(labelLastDivElement.style.pointerEvents).toBe('none'); + const labelSvgGroup = this.querySelector(this.bpmnQuerySelectors.labelSvgGroup(bpmnId)); expectClassAttribute(labelSvgGroup, computeClassValue(bpmnClasses, ['bpmn-label', ...(additionalClasses ?? [])])); }