This is an automated email from the ASF dual-hosted git repository.
jomarko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 122a3e04658 kie-tools-issues#4041: Backward incompatibility of BPMN
Editor for gateway outgoing edge condition (#4043)
122a3e04658 is described below
commit 122a3e046581ca2b085cac9ae1bca88dd9ca840b
Author: Jozef Marko <[email protected]>
AuthorDate: Thu Oct 22 10:18:34 2026 +0200
kie-tools-issues#4041: Backward incompatibility of BPMN Editor for gateway
outgoing edge condition (#4043)
---
packages/bpmn-editor/src/i18n/BpmnEditorI18n.ts | 1 +
packages/bpmn-editor/src/i18n/locales/en.ts | 1 +
.../src/propertiesPanel/codeInput/CodeInput.tsx | 99 +++++++++++++++-------
.../ConditionalExpressionSelector.tsx | 3 +-
.../multiInstance/MultiInstanceProperties.tsx | 3 +-
.../OnEntryAndExitScriptsFormSection.tsx | 4 +-
.../src/propertiesPanel/scriptFormats.ts | 6 ++
.../SequenceFlowProperties.tsx | 31 ++++++-
.../AdHocSubProcessProperties.tsx | 5 +-
.../singleNodeProperties/ScriptTaskProperties.tsx | 2 +-
10 files changed, 115 insertions(+), 40 deletions(-)
diff --git a/packages/bpmn-editor/src/i18n/BpmnEditorI18n.ts
b/packages/bpmn-editor/src/i18n/BpmnEditorI18n.ts
index ce1abc1df9e..7a08210bb49 100644
--- a/packages/bpmn-editor/src/i18n/BpmnEditorI18n.ts
+++ b/packages/bpmn-editor/src/i18n/BpmnEditorI18n.ts
@@ -127,6 +127,7 @@ interface BpmnEditorDictionary extends ReferenceDictionary<{
calledElement: string;
codePlaceholder: string;
conditionalExpression: string;
+ language: string;
defaultRoute: string;
none: string;
noneYet: string;
diff --git a/packages/bpmn-editor/src/i18n/locales/en.ts
b/packages/bpmn-editor/src/i18n/locales/en.ts
index ee5caada878..c5a1cabcff9 100644
--- a/packages/bpmn-editor/src/i18n/locales/en.ts
+++ b/packages/bpmn-editor/src/i18n/locales/en.ts
@@ -133,6 +133,7 @@ export const en: BpmnEditorI18n = {
calledElement: "Called element",
codePlaceholder: "Enter code...",
conditionalExpression: "Conditional Expression",
+ language: "Language",
defaultRoute: "Default route",
none: "None",
noneYet: "None yet",
diff --git a/packages/bpmn-editor/src/propertiesPanel/codeInput/CodeInput.tsx
b/packages/bpmn-editor/src/propertiesPanel/codeInput/CodeInput.tsx
index 208875d4f88..ca10d7f539a 100644
--- a/packages/bpmn-editor/src/propertiesPanel/codeInput/CodeInput.tsx
+++ b/packages/bpmn-editor/src/propertiesPanel/codeInput/CodeInput.tsx
@@ -21,53 +21,92 @@ import * as React from "react";
import { useBpmnEditorStore } from "../../store/StoreContext";
import { FormGroup } from "@patternfly/react-core/dist/js/components/Form";
import { Label } from "@patternfly/react-core/dist/js/components/Label";
+import { MenuToggle } from
"@patternfly/react-core/dist/js/components/MenuToggle";
+import { Select, SelectList, SelectOption } from
"@patternfly/react-core/dist/js/components/Select";
import { TextArea } from "@patternfly/react-core/dist/js/components/TextArea";
import "./CodeInput.css";
import { useBpmnEditorI18n } from "../../i18n";
+import { ScriptLanguage } from "../scriptFormats";
export function CodeInput({
label,
- languages,
+ availableLanguages,
+ currentLanguage,
value,
onChange,
+ onLanguageChange,
}: {
label: string;
- languages: string[];
+ availableLanguages: ScriptLanguage[];
+ /** Currently selected language. Defaults to the first of `languages`. */
+ currentLanguage?: ScriptLanguage;
value: string | undefined;
onChange: (e: React.FormEvent, newCode: string) => void;
+ /** When provided, a dropdown to select the language is shown instead of a
static label. */
+ onLanguageChange?: (newLanguage: ScriptLanguage) => void;
}) {
const { i18n } = useBpmnEditorI18n();
const isReadOnly = useBpmnEditorStore((s) => s.settings.isReadOnly);
+ const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] =
React.useState(false);
- const languageLabel = React.useMemo(() => {
- return (
- <Label
- isCompact={true}
- style={{
- margin: "16px",
- top: "24px",
- right: "0",
- position: "absolute",
- }}
- >
- {languages[0]}
- </Label>
- );
- }, [languages]);
+ const selectedLanguage = currentLanguage ?? availableLanguages[0];
+
+ const hasLanguageSelector = !!onLanguageChange && availableLanguages.length
> 1;
+ const languageSelectId = `${label}-language-select`;
return (
- <FormGroup label={`${label}`} style={{ position: "relative" }}>
- <TextArea
- aria-label={`${label}`}
- type={"text"}
- isDisabled={isReadOnly}
- value={value}
- onChange={onChange}
- placeholder={i18n.propertiesPanel.codePlaceholder}
- style={{ resize: "vertical", minHeight: "40px", fontFamily:
"monospace" }}
- rows={3}
- />
- {languageLabel}
- </FormGroup>
+ <>
+ <FormGroup label={`${label}`}>
+ <TextArea
+ aria-label={`${label}`}
+ type={"text"}
+ isDisabled={isReadOnly}
+ value={value}
+ onChange={onChange}
+ placeholder={i18n.propertiesPanel.codePlaceholder}
+ style={{ resize: "vertical", minHeight: "40px", fontFamily:
"monospace" }}
+ rows={3}
+ />
+ {!hasLanguageSelector && (
+ <div style={{ display: "flex", justifyContent: "flex-end",
marginTop: "4px" }}>
+ <Label isCompact={true}>{selectedLanguage}</Label>
+ </div>
+ )}
+ </FormGroup>
+ {hasLanguageSelector && (
+ <FormGroup label={i18n.propertiesPanel.language}
fieldId={languageSelectId}>
+ <Select
+ id={languageSelectId}
+ isOpen={isLanguageDropdownOpen}
+ selected={selectedLanguage}
+ onOpenChange={setIsLanguageDropdownOpen}
+ onSelect={(_event, selection) => {
+ onLanguageChange(selection as ScriptLanguage);
+ setIsLanguageDropdownOpen(false);
+ }}
+ popperProps={{ appendTo: document.body, width: "trigger" }}
+ toggle={(toggleRef: React.Ref<any>) => (
+ <MenuToggle
+ ref={toggleRef}
+ isFullWidth={true}
+ isExpanded={isLanguageDropdownOpen}
+ isDisabled={isReadOnly}
+ onClick={() => setIsLanguageDropdownOpen((open) => !open)}
+ >
+ {selectedLanguage}
+ </MenuToggle>
+ )}
+ >
+ <SelectList>
+ {availableLanguages.map((l) => (
+ <SelectOption key={l} value={l}>
+ {l}
+ </SelectOption>
+ ))}
+ </SelectList>
+ </Select>
+ </FormGroup>
+ )}
+ </>
);
}
diff --git
a/packages/bpmn-editor/src/propertiesPanel/conditionalExpression/ConditionalExpressionSelector.tsx
b/packages/bpmn-editor/src/propertiesPanel/conditionalExpression/ConditionalExpressionSelector.tsx
index 6f7740417ba..fcd04b52ba3 100644
---
a/packages/bpmn-editor/src/propertiesPanel/conditionalExpression/ConditionalExpressionSelector.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/conditionalExpression/ConditionalExpressionSelector.tsx
@@ -29,6 +29,7 @@ import { Unpacked } from
"@kie-tools/xyflow-react-kie-diagram/dist/tsExt/tsExt";
import { CodeInput } from "../codeInput/CodeInput";
import { generateUuid } from
"@kie-tools/xyflow-react-kie-diagram/dist/uuid/uuid";
import { useBpmnEditorI18n } from "../../i18n";
+import { ScriptLanguage } from "../scriptFormats";
export type WithConditionalExpression =
| undefined
@@ -77,7 +78,7 @@ export function ConditionalEventSelector({ element }: {
element: WithConditional
value={currentValue}
onChange={handleValueChange}
label={i18n.propertiesPanel.conditionalExpression}
- languages={["drools"]}
+ availableLanguages={[ScriptLanguage.Drools]}
/>
</FormSection>
);
diff --git
a/packages/bpmn-editor/src/propertiesPanel/multiInstance/MultiInstanceProperties.tsx
b/packages/bpmn-editor/src/propertiesPanel/multiInstance/MultiInstanceProperties.tsx
index e2ab08a42a2..0362c4beb88 100644
---
a/packages/bpmn-editor/src/propertiesPanel/multiInstance/MultiInstanceProperties.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/multiInstance/MultiInstanceProperties.tsx
@@ -48,6 +48,7 @@ import {
import { addOrGetIoSpecification } from
"../../mutations/addOrGetIoSpecification";
import { addOrGetItemDefinitions, DEFAULT_DATA_TYPES } from
"../../mutations/addOrGetItemDefinitions";
import { useBpmnEditorI18n } from "../../i18n";
+import { ScriptLanguage } from "../scriptFormats";
export type WithMultiInstanceProperties = Normalized<
ElementFilter<
@@ -144,7 +145,7 @@ export function MultiInstanceProperties({ element }: {
element: WithMultiInstanc
<CodeInput
label={i18n.propertiesPanel.completionCondition}
- languages={["MVEL"]}
+ availableLanguages={[ScriptLanguage.MVEL]}
value={
element?.loopCharacteristics?.__$$element ===
"multiInstanceLoopCharacteristics"
? (element?.loopCharacteristics?.completionCondition?.__$$text ??
"")
diff --git
a/packages/bpmn-editor/src/propertiesPanel/onEntryAndExitScripts/OnEntryAndExitScriptsFormSection.tsx
b/packages/bpmn-editor/src/propertiesPanel/onEntryAndExitScripts/OnEntryAndExitScriptsFormSection.tsx
index c0f85b68514..7f5b972cee0 100644
---
a/packages/bpmn-editor/src/propertiesPanel/onEntryAndExitScripts/OnEntryAndExitScriptsFormSection.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/onEntryAndExitScripts/OnEntryAndExitScriptsFormSection.tsx
@@ -76,7 +76,7 @@ export function OnEntryAndExitScriptsFormSection({ element }:
{ element: WithOnE
<FormSection style={{ paddingLeft: "20px", marginTop: "20px", gap:
0 }}>
<CodeInput
label={i18n.propertiesPanel.onEntry}
- languages={["Java"]}
+ availableLanguages={[ScriptLanguage.Java]}
value={element?.extensionElements?.["drools:onEntry-script"]?.["drools:script"]?.__$$text
|| ""}
onChange={(e, newValue) => {
bpmnEditorStoreApi.setState((s) => {
@@ -99,7 +99,7 @@ export function OnEntryAndExitScriptsFormSection({ element }:
{ element: WithOnE
<br />
<CodeInput
label={i18n.propertiesPanel.onExit}
- languages={["Java"]}
+ availableLanguages={[ScriptLanguage.Java]}
value={element?.extensionElements?.["drools:onExit-script"]?.["drools:script"]?.__$$text
|| ""}
onChange={(e, newValue) => {
bpmnEditorStoreApi.setState((s) => {
diff --git a/packages/bpmn-editor/src/propertiesPanel/scriptFormats.ts
b/packages/bpmn-editor/src/propertiesPanel/scriptFormats.ts
index 93e5fba1632..e9c5d5fe451 100644
--- a/packages/bpmn-editor/src/propertiesPanel/scriptFormats.ts
+++ b/packages/bpmn-editor/src/propertiesPanel/scriptFormats.ts
@@ -34,3 +34,9 @@ const SCRIPT_LANGUAGE_FORMATS: Record<ScriptLanguage, string>
= {
export function getScriptFormat(language: ScriptLanguage): string {
return SCRIPT_LANGUAGE_FORMATS[language];
}
+
+export function getScriptLanguage(format: string | undefined): ScriptLanguage
| undefined {
+ return (Object.keys(SCRIPT_LANGUAGE_FORMATS) as ScriptLanguage[]).find(
+ (language) => SCRIPT_LANGUAGE_FORMATS[language] === format
+ );
+}
diff --git
a/packages/bpmn-editor/src/propertiesPanel/singleEdgeProperties/SequenceFlowProperties.tsx
b/packages/bpmn-editor/src/propertiesPanel/singleEdgeProperties/SequenceFlowProperties.tsx
index e4cae9145f4..0d2dbcc76d1 100644
---
a/packages/bpmn-editor/src/propertiesPanel/singleEdgeProperties/SequenceFlowProperties.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/singleEdgeProperties/SequenceFlowProperties.tsx
@@ -29,6 +29,7 @@ import { CodeInput } from "../codeInput/CodeInput";
import { TextInput } from
"@patternfly/react-core/dist/js/components/TextInput/TextInput";
import { generateUuid } from
"@kie-tools/xyflow-react-kie-diagram/dist/uuid/uuid";
import { useBpmnEditorI18n } from "../../i18n";
+import { ScriptLanguage, getScriptFormat, getScriptLanguage } from
"../scriptFormats";
export function SequenceFlowProperties({
sequenceFlow,
@@ -39,6 +40,9 @@ export function SequenceFlowProperties({
const bpmnEditorStoreApi = useBpmnEditorStoreApi();
const isReadOnly = bpmnEditorStoreApi((s) => s.settings.isReadOnly);
+ const currentLanguage =
+ getScriptLanguage((sequenceFlow.conditionExpression as
any)?.["@_language"]) ?? ScriptLanguage.Java;
+
return (
<FormSection>
<NameDocumentationAndId element={sequenceFlow} />
@@ -65,9 +69,24 @@ export function SequenceFlowProperties({
</FormGroup>
<CodeInput
label={i18n.propertiesPanel.conditionalExpression}
- languages={["Java", "MVEL", "DROOLS", "FEEL"]}
+ availableLanguages={[ScriptLanguage.Java, ScriptLanguage.MVEL,
ScriptLanguage.Drools, ScriptLanguage.FEEL]}
+ currentLanguage={currentLanguage}
+ onLanguageChange={(newLanguage) =>
+ bpmnEditorStoreApi.setState((s) => {
+ const { process } = addOrGetProcessAndDiagramElements({
definitions: s.bpmn.model.definitions });
+ visitFlowElementsAndArtifacts(process, ({ element: e }) => {
+ if (e["@_id"] === sequenceFlow["@_id"] && e.__$$element ===
sequenceFlow.__$$element) {
+ e.conditionExpression ??= {
+ "@_id": generateUuid(),
+ };
+ (e.conditionExpression as any)["@_xsi:type"] =
"tFormalExpression";
+ (e.conditionExpression as any)["@_language"] =
getScriptFormat(newLanguage);
+ }
+ });
+ })
+ }
value={sequenceFlow.conditionExpression?.__$$text || ""}
- onChange={(e, newPriority) =>
+ onChange={(e, newScript) =>
bpmnEditorStoreApi.setState((s) => {
const { process } = addOrGetProcessAndDiagramElements({
definitions: s.bpmn.model.definitions,
@@ -77,7 +96,13 @@ export function SequenceFlowProperties({
e.conditionExpression ??= {
"@_id": generateUuid(),
};
- e.conditionExpression.__$$text = newPriority;
+ e.conditionExpression.__$$text = newScript;
+ (e.conditionExpression as any)["@_xsi:type"] =
"tFormalExpression";
+ // Derive the language from the element's current state inside
the setState callback
+ // rather than the render-time `currentLanguage` closure, so a
language change
+ // committed just before this keystroke is not overwritten by
a stale captured value.
+ const language = getScriptLanguage((e.conditionExpression as
any)["@_language"]) ?? ScriptLanguage.Java;
+ (e.conditionExpression as any)["@_language"] ??=
getScriptFormat(language);
}
});
})
diff --git
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/AdHocSubProcessProperties.tsx
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/AdHocSubProcessProperties.tsx
index db4ea8fa936..828526a298c 100644
---
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/AdHocSubProcessProperties.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/AdHocSubProcessProperties.tsx
@@ -45,6 +45,7 @@ import { AsyncCheckbox } from
"../asyncCheckbox/AsyncCheckbox";
import { generateUuid } from
"@kie-tools/xyflow-react-kie-diagram/dist/uuid/uuid";
import { useBpmnEditorI18n } from "../../i18n";
import { ToggleGroup, ToggleGroupItem } from
"@patternfly/react-core/dist/js/components/ToggleGroup";
+import { ScriptLanguage } from "../scriptFormats";
export function AdHocSubProcessProperties({
adHocSubProcess,
@@ -124,7 +125,7 @@ export function AdHocSubProcessProperties({
<CodeInput
label={i18n.singleNodeProperties.adhocActivationCondition}
- languages={["Drools"]}
+ availableLanguages={[ScriptLanguage.Drools]}
value={parseBpmn20Drools10MetaData(adHocSubProcess).get("customActivationCondition")
?? ""}
onChange={(e, newCode) => {
bpmnEditorStoreApi.setState((s) => {
@@ -142,7 +143,7 @@ export function AdHocSubProcessProperties({
<CodeInput
label={i18n.singleNodeProperties.adhocCompletionCondition}
- languages={["MVEL", "Drools"]}
+ availableLanguages={[ScriptLanguage.MVEL, ScriptLanguage.Drools]}
value={adHocSubProcess.completionCondition?.__$$text ?? ""}
onChange={(e, newCompletionConnditionValue) => {
bpmnEditorStoreApi.setState((s) => {
diff --git
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ScriptTaskProperties.tsx
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ScriptTaskProperties.tsx
index 802af70d1a0..35925e90af9 100644
---
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ScriptTaskProperties.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ScriptTaskProperties.tsx
@@ -51,7 +51,7 @@ export function ScriptTaskProperties({
<Divider inset={{ default: "insetXs" }} />
<CodeInput
label={i18n.singleNodeProperties.script}
- languages={["Java"]}
+ availableLanguages={[ScriptLanguage.Java]}
value={scriptTask?.script?.__$$text || ""}
onChange={(e, newScript) => {
bpmnEditorStoreApi.setState((s) => {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]