This is an automated email from the ASF dual-hosted git repository.

ljmotta 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 334d9ff6e9b kie-issues#2244: KIE Sandbox: DMN Runner Outputs table 
stealing the focus from Inputs table (#3440)
334d9ff6e9b is described below

commit 334d9ff6e9b7f6bdc376f3ef588ab963eca81068
Author: Luiz João Motta <[email protected]>
AuthorDate: Wed Feb 18 06:18:20 2026 -0300

    kie-issues#2244: KIE Sandbox: DMN Runner Outputs table stealing the focus 
from Inputs table (#3440)
---
 .../src/selection/BeeTableSelectionContext.tsx     |  17 ++-
 .../table/BeeTable/BeeTableEditableCellContent.tsx |  17 ++-
 .../tests-e2e/api/expressionContainer.ts           |   4 +
 .../api/expressions/contextExpressionElement.ts    |   8 ++
 .../tests-e2e/features/keyboard/keyboard.spec.ts   | 121 ++++++++++++++++++---
 packages/unitables/src/Unitables.css               |   4 +
 packages/unitables/src/Unitables.tsx               |   2 +-
 packages/unitables/src/bee/UnitablesBeeTable.tsx   |  10 ++
 8 files changed, 167 insertions(+), 16 deletions(-)

diff --git 
a/packages/boxed-expression-component/src/selection/BeeTableSelectionContext.tsx
 
b/packages/boxed-expression-component/src/selection/BeeTableSelectionContext.tsx
index 56744e90f23..1ebab272b2a 100644
--- 
a/packages/boxed-expression-component/src/selection/BeeTableSelectionContext.tsx
+++ 
b/packages/boxed-expression-component/src/selection/BeeTableSelectionContext.tsx
@@ -1105,7 +1105,22 @@ export function useBeeTableSelectableCell(
 
   useLayoutEffect(() => {
     if (isActive && !isEditing) {
-      cellRef.current?.focus();
+      const cellElement = cellRef.current;
+      if (!cellElement) {
+        return;
+      }
+
+      // Find the boxed-expression-provider container (top-level container for 
this component)
+      const activeElement = document.activeElement;
+      const boxedExpressionProvider = 
cellElement.closest(".boxed-expression-provider");
+
+      // Don't steal focus if the active element is outside this 
boxed-expression-provider.
+      // This prevents stealing focus from input fields in other 
tables/components when the table re-renders.
+      if (activeElement && boxedExpressionProvider && 
!boxedExpressionProvider.contains(activeElement)) {
+        return;
+      }
+
+      cellElement.focus();
     }
   }, [columnIndex, isActive, isEditing, rowIndex, cellRef]);
 
diff --git 
a/packages/boxed-expression-component/src/table/BeeTable/BeeTableEditableCellContent.tsx
 
b/packages/boxed-expression-component/src/table/BeeTable/BeeTableEditableCellContent.tsx
index db0f597d8d4..fe54bd1cfcc 100644
--- 
a/packages/boxed-expression-component/src/table/BeeTable/BeeTableEditableCellContent.tsx
+++ 
b/packages/boxed-expression-component/src/table/BeeTable/BeeTableEditableCellContent.tsx
@@ -174,7 +174,22 @@ export function BeeTableEditableCellContent({
   const editableCellRef = useRef<HTMLDivElement>(null);
   useEffect(() => {
     if (isActive && !isEditing) {
-      editableCellRef.current?.focus();
+      const cellElement = editableCellRef.current;
+      if (!cellElement) {
+        return;
+      }
+
+      // Find the boxed-expression-provider container (top-level container for 
this component)
+      const activeElement = document.activeElement;
+      const boxedExpressionProvider = 
cellElement.closest(".boxed-expression-provider");
+
+      // Don't steal focus if the active element is outside this 
boxed-expression-provider.
+      // This prevents stealing focus from input fields in other 
tables/components when the table re-renders.
+      if (activeElement && boxedExpressionProvider && 
!boxedExpressionProvider.contains(activeElement)) {
+        return;
+      }
+
+      cellElement.focus();
     }
   }, [isActive, isEditing]);
 
diff --git 
a/packages/boxed-expression-component/tests-e2e/api/expressionContainer.ts 
b/packages/boxed-expression-component/tests-e2e/api/expressionContainer.ts
index 22121236cea..60256b5a158 100644
--- a/packages/boxed-expression-component/tests-e2e/api/expressionContainer.ts
+++ b/packages/boxed-expression-component/tests-e2e/api/expressionContainer.ts
@@ -116,6 +116,10 @@ export class ExpressionCell {
     await this.content.click({ position: { x: 1, y: 1 } });
   }
 
+  // public async isSelected() {
+  //   return this.content.locator(("data-cell.active")).isVisible()
+  // }
+
   public get content() {
     return this.locator.nth(0);
   }
diff --git 
a/packages/boxed-expression-component/tests-e2e/api/expressions/contextExpressionElement.ts
 
b/packages/boxed-expression-component/tests-e2e/api/expressions/contextExpressionElement.ts
index 7e7b05f7e0a..351ae63f931 100644
--- 
a/packages/boxed-expression-component/tests-e2e/api/expressions/contextExpressionElement.ts
+++ 
b/packages/boxed-expression-component/tests-e2e/api/expressions/contextExpressionElement.ts
@@ -40,6 +40,10 @@ export class ContextExpressionElement {
     return new 
ChildExpression(this.locator.getByTestId(`kie-tools--bee--additional-row`).nth(0),
 this.monaco);
   }
 
+  get resultExpressionContainer() {
+    return this.result.elementCell.locator("..");
+  }
+
   public async entriesCount() {
     return (await this.locator.getByRole("row").count()) - 2;
   }
@@ -98,6 +102,10 @@ export class ContextExpressionEntry {
     return this.childExpression.expression;
   }
 
+  get contextExpressionContainer() {
+    return this.childExpression.elementCell.locator("..").locator("..");
+  }
+
   get selectExpressionMenu() {
     return this.childExpression.selectExpressionMenu;
   }
diff --git 
a/packages/boxed-expression-component/tests-e2e/features/keyboard/keyboard.spec.ts
 
b/packages/boxed-expression-component/tests-e2e/features/keyboard/keyboard.spec.ts
index 7e5c8b8d9c1..aca62ca4391 100644
--- 
a/packages/boxed-expression-component/tests-e2e/features/keyboard/keyboard.spec.ts
+++ 
b/packages/boxed-expression-component/tests-e2e/features/keyboard/keyboard.spec.ts
@@ -17,25 +17,120 @@
  * under the License.
  */
 
-import { test } from "../../__fixtures__/base";
+import { expect, test } from "../../__fixtures__/base";
+
+const ACTIVE_CLASS_REGEXP = /(^|\s)active(\s|$)/;
 
 test.describe("Keyboard", () => {
-  test.skip(true, "https://github.com/apache/incubator-kie-issues/issues/542";);
   test.describe("Navigation", () => {
-    test("should correctly navigate", async () => {
-      // enter, shift+enter, tab, shift+tab, escape
-    });
+    test("should correctly navigate", async ({ bee, page, useCases }) => {
+      await useCases.openLoanOriginations("bureau-strategy-decision-service", 
"bureau-call-type");
+      const decisionTable = bee.expression.asDecisionTable();
+      await decisionTable.cellAt({ row: 1, column: 1 }).select();
+
+      await page.keyboard.press("ArrowRight");
+      await expect(decisionTable.cellAt({ row: 1, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowDown");
+      await expect(decisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowUp");
+      await expect(decisionTable.cellAt({ row: 1, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowLeft");
+      await expect(decisionTable.cellAt({ row: 1, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.type(`"test"`);
+      await expect(decisionTable.cellAt({ row: 1, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Enter");
+      await expect(decisionTable.cellAt({ row: 1, column: 1 
}).content).toContainText(`"test"`);
+      await expect(decisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
 
-    test.describe("Arrow keys", () => {
-      test("should correctly navigate", async () => {
-        // arrow up/down/left/right
-      });
+      await page.keyboard.type(`"test2"`);
+      await expect(decisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Escape");
+      await expect(decisionTable.cellAt({ row: 2, column: 1 
}).content).not.toContainText(`"test2"`);
+      await expect(decisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Tab");
+      await expect(decisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Tab");
+      await expect(decisionTable.cellAt({ row: 2, column: 3 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Shift+Tab");
+      await expect(decisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Escape");
+      await expect(decisionTable.cellAt({ row: 2, column: 2 
}).content).not.toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Enter");
+      await expect(decisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
     });
+  });
+
+  test.describe("Nested stories", () => {
+    test("should correctly navigate", async ({ bee, page, useCases }) => {
+      await useCases.openLoanOriginations("bureau-strategy-decision-service", 
"pre-bureau-risk-category");
+      const contextExpression = bee.expression.asContext();
+      const literalExpression = 
contextExpression.entry(0).expression.asLiteral();
+      const resultDecisionTable = 
contextExpression.result.expression.asDecisionTable();
+      await resultDecisionTable.cellAt({ row: 1, column: 1 }).select();
+
+      // Check nested decision table
+      await page.keyboard.press("ArrowRight");
+      await expect(resultDecisionTable.cellAt({ row: 1, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowDown");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowUp");
+      await expect(resultDecisionTable.cellAt({ row: 1, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("ArrowLeft");
+      await expect(resultDecisionTable.cellAt({ row: 1, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.type(`"test"`);
+      await expect(resultDecisionTable.cellAt({ row: 1, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Enter");
+      await expect(resultDecisionTable.cellAt({ row: 1, column: 1 
}).content).toContainText(`"test"`);
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.type(`"test2"`);
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Escape");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 1 
}).content).not.toContainText(`"test2"`);
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 1 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Tab");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Tab");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 3 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Shift+Tab");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Escape");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 2 
}).content).not.toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      await page.keyboard.press("Enter");
+      await expect(resultDecisionTable.cellAt({ row: 2, column: 2 
}).content).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      // Selecting context expression result cell (decision table container)
+      await page.keyboard.press("Escape");
+      await 
expect(contextExpression.resultExpressionContainer).toHaveClass(ACTIVE_CLASS_REGEXP);
+
+      // Selecting context expression entry 0 cell (literal expression 
container)
+      await page.keyboard.press("ArrowUp");
+      await 
expect(contextExpression.entry(0).contextExpressionContainer).toHaveClass(ACTIVE_CLASS_REGEXP);
 
-    test.describe("Nested stories", () => {
-      test("should correctly navigate", async () => {
-        // enter, shift+enter, tab, shift+tab, escape
-      });
+      await page.keyboard.press("Enter");
+      await expect(literalExpression.content).toHaveClass(ACTIVE_CLASS_REGEXP);
     });
   });
 });
diff --git a/packages/unitables/src/Unitables.css 
b/packages/unitables/src/Unitables.css
index 39d683b4bbb..aa143ed04a0 100644
--- a/packages/unitables/src/Unitables.css
+++ b/packages/unitables/src/Unitables.css
@@ -17,6 +17,10 @@
  * under the License. 
  */
 
+.unitables-container {
+  display: flex;
+}
+
 .standalone-bee-table .pf-v5-c-select .pf-v5-c-select__toggle::before {
   border-style: hidden;
 }
diff --git a/packages/unitables/src/Unitables.tsx 
b/packages/unitables/src/Unitables.tsx
index e6fdc3ad82b..d9c12d7d05b 100644
--- a/packages/unitables/src/Unitables.tsx
+++ b/packages/unitables/src/Unitables.tsx
@@ -240,7 +240,7 @@ export const Unitables = ({
         ))}
       </div>
       {unitablesColumns.length > 0 && rows.length > 0 && formsDivRendered ? (
-        <div style={{ display: "flex" }} ref={containerRef}>
+        <div className="unitables-container" ref={containerRef}>
           <UnitablesBeeTable
             rowWrapper={rowWrapper}
             scrollableParentRef={scrollableParentRef}
diff --git a/packages/unitables/src/bee/UnitablesBeeTable.tsx 
b/packages/unitables/src/bee/UnitablesBeeTable.tsx
index 4f1bf7835b4..bb546829af5 100644
--- a/packages/unitables/src/bee/UnitablesBeeTable.tsx
+++ b/packages/unitables/src/bee/UnitablesBeeTable.tsx
@@ -692,6 +692,16 @@ function UnitablesBeeTableCell({
       return;
     }
 
+    // Check if focus is outside the unitables container to prevent stealing 
focus from external inputs
+    const activeElement = document.activeElement;
+    const cellElement = cellRef.current;
+    if (cellElement) {
+      const unitablesContainer = cellElement.closest(".unitables-container");
+      if (activeElement && unitablesContainer && 
!unitablesContainer.contains(activeElement)) {
+        return;
+      }
+    }
+
     if (fieldCharacteristics?.isList) {
       if (isSelectFieldOpen) {
         setTimeout(() => {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to