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]

Reply via email to