Repository: appsmithorg/appsmith
Pull request #40679: fix: Refactor validation logic in TableWidgetV2 to improve clarity and correctness
Author: login: rahulbarwal
Labels: name: Bug
description: Something isn't working
name: Table Widget V2
description: Issues related to Table Widget V2
name: ok-to-test
description: Required label for CI
name: Query Widgets & IDE Pod
description: All issues related to Query, JS, Eval, Widgets & IDE
name: Unplanned Work - Q&W
description: Work items that are unplanned
Created: 2025-05-19T06:35:57Z | Merged: 2025-05-21T13:15:25Z


## Description
<ins>Problem</ins>

The table widget skipped validations for number columns when users typed quickly and pressed Enter, accepting invalid values despite min/max constraints.

<ins>Root cause</ins>

The validation logic had an early exit if the value was empty and the column wasn't required. This incorrectly allowed empty values even when min/max constraints were set. Due to async evaluations and timing differences, a quick backspace followed by a new input and Enter led to premature validation success.

<ins>Solution</ins>

This PR handles a refactor of the validation logic in `TableWidgetV2` to improve clarity and correctness.  
- Consolidates checks for required fields and regex validations.  
- Ensures column type validations accurately reflect constraints like min/max.  
- Prevents premature validation passes caused by async evaluation race conditions.

Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.Table"

### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/15160290045>
> Commit: b3022119656954765a79b1e03c5af2a4338a89c4
> <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=15160290045&attempt=1" target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Table`
> Spec:
> <hr>Wed, 21 May 2025 11:35:27 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Bug Fixes**
  - Enhanced validation for editable table cells to better enforce required fields and regex patterns.
  - Improved handling of empty values in required and non-required cells for more accurate validation feedback.
- **Tests**
  - Refactored and reorganized validation test suites for improved clarity and maintainability without changing validation behavior.
  - Updated end-to-end tests to improve timing and synchronization by removing fixed waits and relying on automatic retries.
  - Adjusted inline editing validation tests to reflect updated error visibility expectations during editing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js
index e6e3e81ac3d9..ce4608a54d09 100644
--- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js
+++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js
@@ -18,36 +18,25 @@ describe(
       _.table.toggleColumnEditableViaColSettingsPane("step", "v2", true, false);
 
       _.propPane.UpdatePropertyFieldValue("Valid", "{{editedValue === '#1'}}");
-      cy.wait(500);
-      cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
+      cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "22");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
-      cy.enterTableCellValue(0, 0, "#1");
-      cy.wait(500);
-      cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       _.propPane.UpdatePropertyFieldValue("Valid", "");
 
       _.propPane.UpdatePropertyFieldValue("Regex", "^#1$");
-      cy.wait(500);
-      cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
+      cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "22");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "#1");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       _.propPane.UpdatePropertyFieldValue("Regex", "");
 
       _.propPane.TogglePropertyState("Required", "On");
       cy.enterTableCellValue(0, 0, "22");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "#1");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
 
       cy.get(commonlocators.changeColType).last().click();
@@ -56,37 +45,26 @@ describe(
 
       _.propPane.UpdatePropertyFieldValue("Min", "5");
       cy.enterTableCellValue(0, 0, "6");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "7");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "4");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "3");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "8");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       _.propPane.UpdatePropertyFieldValue("Min", "");
 
       _.propPane.UpdatePropertyFieldValue("Max", "5");
       cy.enterTableCellValue(0, 0, "6");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "7");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "4");
-      cy.wait(500);
-      cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "3");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.enterTableCellValue(0, 0, "8");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       _.propPane.UpdatePropertyFieldValue("Max", "");
 
@@ -113,31 +91,24 @@ describe(
 
       cy.editTableCell(0, 0);
       cy.enterTableCellValue(0, 0, "3");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "2");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.discardTableCellValue(0, 0);
       cy.wait(500);
       cy.get(".t--add-new-row").click();
-      cy.wait(1000);
       cy.enterTableCellValue(0, 0, "3");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "2");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
       cy.enterTableCellValue(0, 0, "1");
-      cy.wait(500);
       cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
       cy.get(".t--discard-new-row").click({ force: true });
     });
 
     it("2.3. should test that validation is working for more than one add new row cell at a time", () => {
       _.propPane.UpdatePropertyFieldValue("Valid", "{{editedValue === 1}}");
-      cy.get("[data-testid='t--property-pane-back-btn']").click();
-      cy.wait(500);
+      _.propPane.NavigateBackToPropertyPane();
       _.table.toggleColumnEditableViaColSettingsPane("task", "v2", true, false);
       _.propPane.UpdatePropertyFieldValue(
         "Valid",
diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js
index cb25d3b66670..01ea2289c4dc 100644
--- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js
+++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js
@@ -87,8 +87,7 @@ describe(
           _.propPane.TogglePropertyState("Editable", "On");
           _.propPane.UpdatePropertyFieldValue("Regex", "^#1$");
           cy.editTableCell(0, 0);
-          cy.wait(500);
-          cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
+          cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "22");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "#1");
@@ -104,8 +103,7 @@ describe(
             "{{editedValue === '#1'}}",
           );
           cy.editTableCell(0, 0);
-          cy.wait(500);
-          cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
+          cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "22");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "#1");
@@ -118,7 +116,6 @@ describe(
           _.propPane.TogglePropertyState("Editable", "On");
           _.propPane.TogglePropertyState("Required", "On");
           cy.editTableCell(0, 0);
-          cy.wait(500);
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "22");
           cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
@@ -146,8 +143,7 @@ describe(
           _.propPane.UpdatePropertyFieldValue("Min", "5");
 
           cy.editTableCell(0, 0);
-          cy.wait(500);
-          cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
+          cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.enterTableCellValue(0, 0, "6");
           cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
           cy.enterTableCellValue(0, 0, "7");
@@ -172,7 +168,6 @@ describe(
           _.propPane.UpdatePropertyFieldValue("Max", "5");
 
           cy.editTableCell(0, 0);
-          cy.wait(500);
           cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
           cy.enterTableCellValue(0, 0, "6");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
@@ -198,7 +193,6 @@ describe(
         "You got error mate!!",
       );
       cy.editTableCell(0, 0);
-      cy.wait(1000);
       cy.enterTableCellValue(0, 0, "123");
       cy.get(".bp3-overlay.error-tooltip .bp3-popover-content").should(
         "contain",
@@ -224,12 +218,12 @@ describe(
           cy.get(`.t--inlined-cell-editor`).should("exist");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.saveTableCellValue(0, 0);
-          cy.wait(500);
           cy.get(`.t--inlined-cell-editor`).should("exist");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.get(widgetsPage.toastAction).should("not.exist");
           cy.enterTableCellValue(0, 0, "#1");
           cy.saveTableCellValue(0, 0);
+          cy.wait(500);
           cy.get(`.t--inlined-cell-editor`).should("not.exist");
           cy.get(`.t--inlined-cell-editor-has-error`).should("not.exist");
           cy.get(widgetsPage.toastAction).should("be.visible");
@@ -276,7 +270,7 @@ describe(
       },
     );
 
-    it("should check that save/discard button is disabled when there is a validation error", () => {
+    it("9. should check that save/discard button is disabled when there is a validation error", () => {
       cy.openPropertyPane("tablewidgetv2");
       cy.editColumn("step");
       _.propPane.TogglePropertyState("Editable", "On");
diff --git a/app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js b/app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js
index 93de27d17be6..16df5932f2a0 100644
--- a/app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js
+++ b/app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js
@@ -4,1244 +4,460 @@ import derivedProperty from "../../derived";
 describe("getEditableCellValidity", () => {
   const { getEditableCellValidity } = derivedProperty;
 
-  it("should test that its returns empty object when editableCell is empty and isAddRowInProgess is false", () => {
-    expect(
-      getEditableCellValidity(
-        {
-          editableCell: {},
-          isAddRowInProgress: false,
-        },
-        null,
-        _,
-      ),
-    ).toEqual({});
-  });
-
-  describe("should test that it validates the editableColumn against all the validation properties", () => {
-    it("should return true for editable column when validation is empty", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
+  describe("1 when isAddRowInProgress is false", () => {
+    it("1.1 should return empty object when editableCell is empty", () => {
       expect(
         getEditableCellValidity(
           {
-            editableCell: {
-              column: "step",
-              value: "123",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-            },
+            editableCell: {},
+            isAddRowInProgress: false,
           },
           null,
           _,
         ),
-      ).toEqual({ step: true });
+      ).toEqual({});
     });
 
-    it("should return true for editable column when isColumnEditableCellRequired is off and there is no value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
+    describe("1.2 validation rules", () => {
+      it("1.2.1 should return true when validation is empty", () => {
+        expect(
+          getEditableCellValidity(
+            {
+              editableCell: {
+                column: "step",
+                value: "test",
               },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
+              primaryColumns: {
+                step: {
+                  columnType: "text",
+                  alias: "step",
+                  validation: {},
+                },
+              },
+            },
+            null,
+            _,
+          ),
+        ).toEqual({ step: true });
+      });
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: undefined,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
+      it("1.2.2 should handle required field validation", () => {
+        // Required field with value
+        const props = {
+          editableCell: {
+            column: "step",
+            value: "test",
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: null,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
+          primaryColumns: {
+            step: {
+              columnType: "text",
+              alias: "step",
+              validation: {
+                isColumnEditableCellRequired: true,
               },
             },
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        };
 
-    it("should return true for editable column when isColumnEditableCellValid is true", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: null,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
 
-    it("should return false for editable column when isColumnEditableCellValid is false", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "test",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
+        // Required field without value
+        props.editableCell.value = "";
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
 
-    it("should return true for editable column when regex is matching", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^#1$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
+        // Not required field without value
+        props.primaryColumns.step.validation.isColumnEditableCellRequired = false;
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+      });
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "test",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^test$",
-                },
+      it("1.2.3 should handle editable column when isColumnEditableCellValid is provided", () => {
+        const props = {
+          editableCell: {
+            column: "step",
+            value: null,
+          },
+          primaryColumns: {
+            step: {
+              columnType: "text",
+              alias: "step",
+              validation: {
+                isColumnEditableCellValid: true,
               },
             },
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        };
 
-    it("should return false for editable column when regex is not matching", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "test",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^#1$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^test$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
+        props.primaryColumns.step.validation.isColumnEditableCellValid = false;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
+      });
+
+      it("1.2.4 should handle regex validation", () => {
+        // Matching regex
+        const props = {
+          editableCell: {
+            column: "step",
+            value: "#1",
+          },
+          primaryColumns: {
+            step: {
+              columnType: "text",
+              alias: "step",
+              validation: {
+                regex: "^#1$",
+              },
+            },
+          },
+        };
+
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+        // Non-matching regex
+        props.editableCell.value = "test";
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
+      });
 
-    it("should return false for editable column when isColumnEditableCellRequired is true and there is no value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
+      it("1.2.5 should handle number/currency column validation", () => {
+        // Min validation
+        const props = {
+          editableCell: {
+            column: "amount",
+            value: 1,
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return true for editable column when isColumnEditableCellRequired and there is value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "test",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
+          primaryColumns: {
+            amount: {
+              columnType: "number",
+              alias: "amount",
+              validation: {
+                min: 0,
+              },
+            },
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        };
 
-    it("should return true for editable column when value is above min", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: 1,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  min: 0,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: true,
+        });
 
-    it("should return false for editable column when value is below min", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: -1,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  min: 0,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
+        props.editableCell.value = -1;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: false,
+        });
 
-    it("should return true for editable column when value is below max", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: 2,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  max: 5,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
+        delete props.primaryColumns.amount.validation.min;
 
-    it("should return false for editable column when value is above max", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: 6,
-            },
-            primaryColumns: {
-              step: {
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  max: 5,
-                },
+        // Max validation
+        props.primaryColumns.amount.validation.max = 5;
+        props.editableCell.value = 4;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: true,
+        });
+
+        props.editableCell.value = 6;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: false,
+        });
+      });
+
+      it("1.2.6 should handle combined validation rules", () => {
+        const props = {
+          editableCell: {
+            column: "step",
+            value: "#1",
+          },
+          primaryColumns: {
+            step: {
+              columnType: "text",
+              alias: "step",
+              validation: {
+                isColumnEditableCellValid: false,
+                regex: "^#1$",
+                isColumnEditableCellRequired: false,
               },
             },
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
+        };
+
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
+
+        props.primaryColumns.step.validation.isColumnEditableCellValid = true;
+        props.editableCell.value = "#1";
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+
+        props.editableCell.value = "#2";
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
+      });
     });
+  });
 
-    it("should return true for editable column when value is matching all the validation criteria", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: false,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
+  describe("2 when isAddRowInProgress is true", () => {
+    it("2.1 should validate only editable columns", () => {
+      const props = {
+        isAddRowInProgress: true,
+        editableCell: {},
+        newRow: {
+          step: "#1",
+          task: "test",
+        },
+        primaryColumns: {
+          step: {
+            isEditable: true,
+            columnType: "text",
+            alias: "step",
+            validation: {},
+          },
+          task: {
+            columnType: "text",
+            alias: "task",
+            validation: {},
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
+        },
+      };
+
+      expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+      props.primaryColumns.task.isEditable = true;
+      expect(getEditableCellValidity(props, null, _)).toEqual({
+        step: true,
+        task: true,
+      });
+    });
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
+    describe("2.2 validation rules", () => {
+      it("2.2.1 should return true when validation is empty", () => {
+        expect(
+          getEditableCellValidity(
+            {
+              isAddRowInProgress: true,
+              editableCell: {},
+              newRow: {
+                step: "test",
+                task: "test",
+              },
+              primaryColumns: {
+                step: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "step",
+                  validation: {},
+                },
+                task: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "task",
+                  validation: {},
+                },
+              },
+            },
+            null,
+            _,
+          ),
+        ).toEqual({ step: true, task: true });
+      });
+
+      it("2.2.2 should handle required field validation", () => {
+        const props = {
+          isAddRowInProgress: true,
+          editableCell: {},
+          newRow: {
+            step: "test",
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: true,
-                },
+          primaryColumns: {
+            step: {
+              isEditable: true,
+              columnType: "text",
+              alias: "step",
+              validation: {
+                isColumnEditableCellRequired: true,
               },
             },
           },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
+        };
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#2$",
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
+        // Required field with value
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
 
-      expect(
-        getEditableCellValidity(
-          {
-            editableCell: {
-              column: "step",
-              value: "#1",
-            },
-            primaryColumns: {
-              step: {
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#2$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-  });
+        // Required field without value
+        props.newRow.step = "";
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
 
-  describe("should test that it validates the new row against all the validation properties", () => {
-    it("should check that only editable columns are present in the validation object", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-              task: {
-                columnType: "text",
-                alias: "task",
-                validation: {},
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
+        // Not required field without value
+        props.primaryColumns.step.validation.isColumnEditableCellRequired = false;
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+      });
 
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-              task: {
-                isEditable: true,
-                columnType: "text",
-                alias: "task",
-                validation: {},
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ task: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-              task: {
-                isEditable: true,
-                columnType: "text",
-                alias: "task",
-                validation: {},
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true, task: true });
-    });
-
-    it("should return true for editable columns when validation is empty", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {},
-              },
-              task: {
-                isEditable: true,
-                columnType: "text",
-                alias: "task",
-                validation: {},
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true, task: true });
-    });
-
-    it("should return true for editable columns when isColumnEditableCellRequired is off and there is no value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: undefined,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: null,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return true for editable columns when isColumnEditableCellValid is true", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: null,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return false for editable columns when isColumnEditableCellValid is false", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return true for editable columns when regex is matching", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^#1$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^test$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return false for editable columns when regex is not matching", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^#1$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  regex: "^test$",
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return false for editable columns when isColumnEditableCellRequired is true and there is no value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return true for editable columns when isColumnEditableCellRequired and there is value", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return true for editable columns when value is above min", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: 1,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  min: 0,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return false for editable columns when value is below min", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: -1,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  min: 0,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return true for editable columns when value is below max", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: 2,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  max: 5,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-    });
-
-    it("should return false for editable columns when value is above max", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: 6,
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "number",
-                alias: "step",
-                validation: {
-                  max: 5,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should return true for editable columns when value is matching all the validation criteria", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: false,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#2$",
-                  isColumnEditableCellRequired: true,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                alias: "step",
-                columnType: "text",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#2$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false });
-    });
-
-    it("should check that more than one column is validated at the same time", () => {
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: false,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-              task: {
-                isEditable: true,
-                columnType: "text",
-                alias: "task",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "test",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: false, task: true });
-
-      expect(
-        getEditableCellValidity(
-          {
-            isAddRowInProgress: true,
-            editableCell: {},
-            newRow: {
-              step: "#1",
-              task: "test",
-            },
-            primaryColumns: {
-              step: {
-                isEditable: true,
-                columnType: "text",
-                alias: "step",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "^#1$",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-              task: {
-                isEditable: true,
-                columnType: "text",
-                alias: "task",
-                validation: {
-                  isColumnEditableCellValid: true,
-                  regex: "test",
-                  isColumnEditableCellRequired: false,
-                },
-              },
-            },
-          },
-          null,
-          _,
-        ),
-      ).toEqual({ step: true, task: true });
+      it("2.2.3 should handle editable column when isColumnEditableCellValid is provided", () => {
+        const props = {
+          isAddRowInProgress: true,
+          editableCell: {},
+          newRow: {
+            step: null,
+          },
+          primaryColumns: {
+            step: {
+              isEditable: true,
+              columnType: "text",
+              alias: "step",
+              validation: {
+                isColumnEditableCellValid: true,
+              },
+            },
+          },
+        };
+
+        expect(getEditableCellValidity(props, null, _)).toEqual({ step: true });
+
+        props.primaryColumns.step.validation.isColumnEditableCellValid = false;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          step: false,
+        });
+      });
+
+      it("2.2.4 should handle regex validation", () => {
+        // Matching regex
+        expect(
+          getEditableCellValidity(
+            {
+              isAddRowInProgress: true,
+              editableCell: {},
+              newRow: {
+                step: "#1",
+              },
+              primaryColumns: {
+                step: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "step",
+                  validation: {
+                    regex: "^#1$",
+                  },
+                },
+              },
+            },
+            null,
+            _,
+          ),
+        ).toEqual({ step: true });
+
+        // Non-matching regex
+        expect(
+          getEditableCellValidity(
+            {
+              isAddRowInProgress: true,
+              editableCell: {},
+              newRow: {
+                step: "test",
+              },
+              primaryColumns: {
+                step: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "step",
+                  validation: {
+                    regex: "^#1$",
+                  },
+                },
+              },
+            },
+            null,
+            _,
+          ),
+        ).toEqual({ step: false });
+      });
+
+      it("2.2.5 should handle number/currency column validation", () => {
+        // Min validation
+        const props = {
+          isAddRowInProgress: true,
+          editableCell: {},
+          newRow: {
+            amount: 1,
+          },
+          primaryColumns: {
+            amount: {
+              isEditable: true,
+              columnType: "number",
+              alias: "amount",
+              validation: {
+                min: 0,
+              },
+            },
+          },
+        };
+
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: true,
+        });
+
+        props.newRow.amount = -1;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: false,
+        });
+
+        delete props.primaryColumns.amount.validation.min;
+
+        // Max validation
+        props.primaryColumns.amount.validation.max = 5;
+        props.newRow.amount = 4;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: true,
+        });
+
+        props.newRow.amount = 6;
+        expect(getEditableCellValidity(props, null, _)).toEqual({
+          amount: false,
+        });
+      });
+
+      it("2.2.6 should handle multiple columns validation", () => {
+        expect(
+          getEditableCellValidity(
+            {
+              isAddRowInProgress: true,
+              editableCell: {},
+              newRow: {
+                step: "#1",
+                task: "test",
+              },
+              primaryColumns: {
+                step: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "step",
+                  validation: {
+                    isColumnEditableCellValid: true,
+                    regex: "^#1$",
+                    isColumnEditableCellRequired: true,
+                  },
+                },
+                task: {
+                  isEditable: true,
+                  columnType: "text",
+                  alias: "task",
+                  validation: {
+                    isColumnEditableCellValid: true,
+                    regex: "test",
+                    isColumnEditableCellRequired: true,
+                  },
+                },
+              },
+            },
+            null,
+            _,
+          ),
+        ).toEqual({ step: true, task: true });
+      });
     });
   });
 });
diff --git a/app/client/src/widgets/TableWidgetV2/widget/derived.js b/app/client/src/widgets/TableWidgetV2/widget/derived.js
index dbc30922a56f..b4c52929d1f1 100644
--- a/app/client/src/widgets/TableWidgetV2/widget/derived.js
+++ b/app/client/src/widgets/TableWidgetV2/widget/derived.js
@@ -1199,62 +1199,60 @@ export default {
 
     const validationMap = {};
 
-    editableColumns.forEach((validationObj) => {
-      const editedColumn = validationObj[0];
-      const value = validationObj[1];
+    editableColumns.forEach(([editedColumn, value]) => {
+      let isValid = true;
 
       if (editedColumn && editedColumn.validation) {
         const validation = editedColumn.validation;
 
-        /* General validations */
+        /**
+         * General validations
+         * 1. isColumnEditableCellValid
+         * 2. regex
+         * 3. isColumnEditableCellRequired
+         * 4. number/currency min/max
+         */
         if (
-          !validation.isColumnEditableCellRequired &&
-          (value === "" || _.isNil(value))
+          !_.isNil(validation.isColumnEditableCellValid) &&
+          !validation.isColumnEditableCellValid
         ) {
-          validationMap[editedColumn.alias] = true;
-
-          return;
+          isValid = false;
         } else if (
-          (!_.isNil(validation.isColumnEditableCellValid) &&
-            !validation.isColumnEditableCellValid) ||
-          (validation.regex && !createRegex(validation.regex).test(value)) ||
-          (validation.isColumnEditableCellRequired &&
-            (value === "" || _.isNil(value)))
+          validation.regex &&
+          !createRegex(validation.regex).test(value)
         ) {
-          validationMap[editedColumn.alias] = false;
-
-          return;
-        }
-
-        /* Column type related validations */
-        switch (editedColumn.columnType) {
-          case "number":
-          case "currency":
-            if (
-              !_.isNil(validation.min) &&
-              validation.min !== "" &&
-              validation.min > value
-            ) {
-              validationMap[editedColumn.alias] = false;
-
-              return;
-            }
-
-            if (
-              !_.isNil(validation.max) &&
-              validation.max !== "" &&
-              validation.max < value
-            ) {
-              validationMap[editedColumn.alias] = false;
+          isValid = false;
+        } else if (
+          validation.isColumnEditableCellRequired &&
+          (value === "" || _.isNil(value))
+        ) {
+          isValid = false;
+        } else {
+          switch (editedColumn.columnType) {
+            case "number":
+            case "currency":
+              if (
+                !_.isNil(validation.min) &&
+                validation.min !== "" &&
+                validation.min > value
+              ) {
+                isValid = false;
+              }
 
-              return;
-            }
+              if (
+                !_.isNil(validation.max) &&
+                validation.max !== "" &&
+                validation.max < value
+              ) {
+                isValid = false;
+              }
 
-            break;
+              break;
+          }
         }
       }
 
-      validationMap[editedColumn.alias] = true;
+      validationMap[editedColumn.alias] = isValid;
     });
 
     return validationMap;


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js (1)</summary><blockquote>

`1203-1203`: **Consider using optional chaining.**

The static analysis tool suggests using optional chaining here for better safety.

```diff
-      if (editedColumn && editedColumn.validation) {
-        const validation = editedColumn.validation;
+      if (editedColumn?.validation) {
+        const validation = editedColumn.validation;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1203-1203: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</blockquote></details>
<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js (2)</summary><blockquote>

`153-153`: **Consider avoiding the delete operator.**

For better performance, use undefined assignment instead of the delete operator.

```diff
-        delete props.primaryColumns.amount.validation.min;
+        props.primaryColumns.amount.validation.min = undefined;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 153-153: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>

---

`408-408`: **Consider avoiding the delete operator.**

For better performance, use undefined assignment instead of the delete operator.

```diff
-        delete props.primaryColumns.amount.validation.min;
+        props.primaryColumns.amount.validation.min = undefined;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 408-408: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro (Legacy)**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d52ab290a28244ea1089ca4d5630e834d5d435b6 and 44eb8eb733fefc94614eb3443f4b4d0f3d8727c1.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js` (1 hunks)
* `app/client/src/widgets/TableWidgetV2/widget/derived.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js (1)</summary><blockquote>

<details>
<summary>app/client/cypress/support/commands.js (1)</summary>

* `_` (1042-1042)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js</summary>

[error] 153-153: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

---

[error] 408-408: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>
<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js</summary>

[error] 1203-1203: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: client-unit-tests / client-unit-tests
* GitHub Check: client-lint / client-lint
* GitHub Check: client-prettier / prettier-check
* GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
* GitHub Check: client-build / client-build

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js (3)</summary>

`1202-1214`: **Approve the improved validation check order.**

The refactoring correctly prioritizes the explicit validation flag check, ensuring invalid states are caught immediately before proceeding with other validations.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1203-1203: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

---

`1215-1220`: **Good separation of regex validation.**

Separating the regex validation into its own distinct check improves code clarity and maintainability.

---

`1247-1255`: **Critical fix for required field validation.**

Moving the required field validation after type-specific checks ensures that min/max constraints are properly enforced even when values are empty, addressing the core issue where number validations were being skipped.

</details>
<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js (2)</summary>

`7-201`: **Well-structured test organization for non-add-row scenario.**

The test restructuring improves readability by grouping related tests and using consistent patterns. Each validation type is now clearly isolated and tested independently.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 153-153: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>

---

`203-461`: **Comprehensive test coverage for add-row scenario.**

The second test group properly mirrors the first group's structure while focusing on the add-row specific validation logic. This parallel structure makes the tests more maintainable.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 408-408: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T06:28:12Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js (3)</summary><blockquote>

`1205-1220`: **Consider using optional chaining for validation existence check.**

The nested property access could be simplified with optional chaining.

```diff
-if (
-  !_.isNil(validation.isColumnEditableCellValid) &&
-  !validation.isColumnEditableCellValid
-) {
+if (validation?.isColumnEditableCellValid === false) {
  isValid = false;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1205-1205: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

---

`1234-1240`: **Simplify numeric validation condition.**

The validation for minimum value check has redundant null checking.

```diff
if (
-  !_.isNil(validation.min) &&
-  validation.min !== "" &&
+  validation.min !== "" && 
+  validation.min != null &&
  validation.min > value
) {
  isValid = false;
}
```

---

`1226-1230`: **Consider consistent null checking approach.**

The code uses `_.isNil()` in some places and direct equality in others. Consider standardizing the approach across the validation logic.

```diff
else if (
  validation.isColumnEditableCellRequired &&
-  (value === "" || _.isNil(value))
+  (value === "" || value == null)
) {
  isValid = false;
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro (Legacy)**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 44eb8eb733fefc94614eb3443f4b4d0f3d8727c1 and 141603a0be6e31fae6c69783ef0902542cba4c38.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `app/client/src/widgets/TableWidgetV2/widget/derived.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js (2)</summary><blockquote>

<details>
<summary>app/client/src/widgets/JSONFormWidget/fields/CurrencyInputField.tsx (1)</summary>

* `isValid` (49-77)

</details>
<details>
<summary>app/client/cypress/support/commands.js (1)</summary>

* `_` (1042-1042)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js</summary>

[error] 1205-1205: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: client-unit-tests / client-unit-tests
* GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
* GitHub Check: client-lint / client-lint
* GitHub Check: client-build / client-build
* GitHub Check: client-prettier / prettier-check

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>app/client/src/widgets/TableWidgetV2/widget/derived.js (1)</summary>

`1202-1256`: **Good refactoring of validation logic to address race condition issues.**

The validation refactoring makes significant improvements to how the validity checks are performed by:

1. Using a single `isValid` flag to track status instead of multiple early returns
2. Clearly organizing validations with documented prioritization
3. Ensuring minimum and maximum validations are properly applied for numeric fields
4. Preventing validation bypasses when quickly typing and pressing Enter

This addresses the core issue identified in the PR where number column validations could be skipped under certain user interactions.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1205-1205: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T06:59:49Z
user: login: AmanAgarwal041
body: LGTM
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:53:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro (Legacy)**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 141603a0be6e31fae6c69783ef0902542cba4c38 and b3022119656954765a79b1e03c5af2a4338a89c4.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js` (2 hunks)
* `app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js` (5 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`app/client/cypress/**/**.*`: Review the following e2e test code written using the Cypress test library. Ensure that:
- Follow best practices for Cypress code and e2e automation. 
...</summary>

> `app/client/cypress/**/**.*`: Review the following e2e test code written using the Cypress test library. Ensure that:
> - Follow best practices for Cypress code and e2e automation. 
> - Avoid using cy.wait in code.
> - Avoid using cy.pause in code.
> - Avoid using agHelper.sleep().
> - Use locator variables for locators and do not use plain strings.
> - Use data-* attributes for selectors.
> - Avoid Xpaths, Attributes and CSS path.
> - Avoid selectors like .btn.submit or button[type=submit].
> - Perform logins via API with LoginFromAPI.
> - Perform logout via API with LogOutviaAPI.
> - Perform signup via API with SignupFromAPI.
> - Avoid using it.only.
> - Avoid using after and aftereach in test cases.
> - Use multiple assertions for expect statements.
> - Avoid using strings for assertions.
> - Do not use duplicate filenames even with different paths.
> - Avoid using agHelper.Sleep, this.Sleep in any file in code.
> 

- `app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js`
- `app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js`

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (8)</summary>

* GitHub Check: perform-test / client-build / client-build
* GitHub Check: perform-test / rts-build / build
* GitHub Check: perform-test / server-build / server-unit-tests
* GitHub Check: client-unit-tests / client-unit-tests
* GitHub Check: client-lint / client-lint
* GitHub Check: client-check-cyclic-deps / check-cyclic-dependencies
* GitHub Check: client-build / client-build
* GitHub Check: client-prettier / prettier-check

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js (4)</summary>

`90-90`: **Removed explicit wait before assertion**

The removal of explicit wait helps adhere to Cypress best practices. Cypress automatically retries assertions until they pass or timeout, making explicit waits unnecessary in most cases.

---

`106-106`: **Removed explicit wait before assertion**

This change improves test reliability by leveraging Cypress's built-in retry mechanism instead of using fixed waits.

---

`146-146`: **Removed explicit wait before assertion**

Good change. Fixed waits can cause tests to be flaky or slow. Cypress's automatic retries provide better synchronization.

---

`273-273`: **Added numeric prefix to test name**

The addition of "9." prefix improves test organization and makes the test order more explicit.

</details>
<details>
<summary>app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js (3)</summary>

`21-21`: **Removed explicit wait before assertion**

Good practice. Removing explicit waits and relying on Cypress's built-in retry mechanism makes tests more reliable.

---

`27-27`: **Removed explicit wait before assertion**

This change follows Cypress best practices by avoiding fixed waits and letting Cypress handle the timing automatically.

---

`111-111`: **Using helper method for navigation**

Good refactoring. Using the NavigateBackToPropertyPane helper method improves code maintainability and readability compared to manual navigation with explicit waits.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T10:55:10Z
user: login: AmanAgarwal041
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:49:00Z

Review comments:
diff_hunk: @@ -1199,62 +1199,60 @@ export default {
 
     const validationMap = {};
 
-    editableColumns.forEach((validationObj) => {
-      const editedColumn = validationObj[0];
-      const value = validationObj[1];
+    editableColumns.forEach(([editedColumn, value]) => {
+      let isValid = true;
 
       if (editedColumn && editedColumn.validation) {
         const validation = editedColumn.validation;
 
-        /* General validations */
+        /**
+         * General validations
+         * 1. isColumnEditableCellValid
+         * 2. regex
+         * 3. isColumnEditableCellRequired
+         * 4. number/currency min/max
+         */
         if (
-          !validation.isColumnEditableCellRequired &&
-          (value === "" || _.isNil(value))
+          !_.isNil(validation.isColumnEditableCellValid) &&
+          !validation.isColumnEditableCellValid
         ) {
-          validationMap[editedColumn.alias] = true;
-
-          return;
+          isValid = false;
         } else if (
-          (!_.isNil(validation.isColumnEditableCellValid) &&
-            !validation.isColumnEditableCellValid) ||
-          (validation.regex && !createRegex(validation.regex).test(value)) ||
-          (validation.isColumnEditableCellRequired &&
-            (value === "" || _.isNil(value)))
+          validation.regex &&
+          !createRegex(validation.regex).test(value)
         ) {
-          validationMap[editedColumn.alias] = false;
-
-          return;
-        }
-
-        /* Column type related validations */
-        switch (editedColumn.columnType) {
-          case "number":
-          case "currency":
-            if (
-              !_.isNil(validation.min) &&
-              validation.min !== "" &&
-              validation.min > value
-            ) {
-              validationMap[editedColumn.alias] = false;
-
-              return;
-            }
-
-            if (
-              !_.isNil(validation.max) &&
-              validation.max !== "" &&
-              validation.max < value
-            ) {
-              validationMap[editedColumn.alias] = false;
+          isValid = false;
+        } else if (
+          validation.isColumnEditableCellRequired &&
+          (value === "" || _.isNil(value))
+        ) {
+          isValid = false;
+        } else {
+          switch (editedColumn.columnType) {
+            case "number":
+            case "currency":
+              if (
+                !_.isNil(validation.min) &&
+                validation.min !== "" &&
+                validation.min > value
+              ) {
+                isValid = false;
+              }
 
-              return;
-            }
+              if (
+                !_.isNil(validation.max) &&
+                validation.max !== "" &&
+                validation.max < value
+              ) {
+                isValid = false;
+              }
 
-            break;
+              break;
+          }
         }
path: app/client/src/widgets/TableWidgetV2/widget/derived.js
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify numeric validation with empty values.**

The current validation flow may still allow empty values to bypass min/max validations when the field isn't required. You should verify that the empty value handling works as expected for non-required numeric fields.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for empty value handling in the test file

# Look for tests that verify empty value handling with min/max constraints
rg -A 10 "min|max.*empty" app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js
```

Length of output: 1340

---



</details>

**Add tests for empty-string handling in numeric validation**

The existing tests cover `null` values but don’t explicitly verify that an empty string (`""`) bypasses `min`/`max` when the field isn’t required. Please add test cases in `getEditableCellValidity.test.js` to ensure empty strings are treated as valid for non-required numeric columns:

• In `app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js`  
  – Under the “Min validation” block, after the `null` check, add:  
    ```js
    // Empty-string value (non-required) should bypass min/max
    props.editableCell.value = "";
    expect(getEditableCellValidity(props, null, _)).toEqual({ amount: true });
    ```  

  – Similarly, under the “Max validation” block, add an empty-string check for `newRow`:  
    ```js
    props.newRow.amount = "";
    expect(getEditableCellValidity(props, null, _)).toEqual({ amount: true });
    ```  

This ensures empty strings don’t get erroneously flagged by numeric min/max logic when the field is optional.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js,
add test cases for empty-string handling in numeric validation under the "Min
validation" and "Max validation" blocks. Specifically, after the existing null
checks, add tests that set the editable cell value or newRow amount to an empty
string ("") and assert that getEditableCellValidity returns true for those
cases. This will verify that empty strings bypass min/max validation when the
numeric field is not required.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-20T06:59:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -224,12 +218,12 @@ describe(
           cy.get(`.t--inlined-cell-editor`).should("exist");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.saveTableCellValue(0, 0);
-          cy.wait(500);
           cy.get(`.t--inlined-cell-editor`).should("exist");
           cy.get(`.t--inlined-cell-editor-has-error`).should("exist");
           cy.get(widgetsPage.toastAction).should("not.exist");
           cy.enterTableCellValue(0, 0, "#1");
           cy.saveTableCellValue(0, 0);
+          cy.wait(500);
path: app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Remove explicit wait after saving cell value**

This explicit wait contradicts the pattern of removing waits in other parts of the file and violates the coding guideline to avoid using cy.wait().

```diff
-          cy.wait(500);
+          cy.get(`.t--inlined-cell-editor`).should("not.exist");
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
          cy.get(`.t--inlined-cell-editor`).should("not.exist");
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js
at line 226, remove the explicit cy.wait(500) call after saving the cell value.
Replace it with a more reliable Cypress command that waits for a specific
condition or element state to ensure the save operation is complete, maintaining
consistency with the rest of the file and adhering to the guideline against
using cy.wait().
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-21T10:55:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-19T06:36:05Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The validation logic within the `getEditableCellValidity` function was refactored. The initial validity check was changed to immediately mark invalid if an explicit validity flag is false. Regex validation was separated from other checks, and required field validation was explicitly enforced after type-specific validations. The test suite was reorganized into grouped blocks for better clarity and maintainability. No changes were made to exported function signatures. Additionally, Cypress tests were updated to remove explicit waits and rely on built-in retry mechanisms, and some test assertions were adjusted to better reflect validation error states.

## Changes

| File(s)                                                                 | Change Summary                                                                                                               |
|-------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| app/client/src/widgets/TableWidgetV2/widget/derived.js                 | Refactored `getEditableCellValidity`: reordered validation checks, separated regex validation, explicitly enforced required field validation, and removed early acceptance of empty non-required values. No public API changes. |
| app/client/src/widgets/TableWidgetV2/widget/__tests__/derived.test/getEditableCellValidity.test.js | Reorganized and refactored test suite for `getEditableCellValidity` into grouped describe blocks based on `isAddRowInProgress` and validation scenarios, improving readability without changing test coverage or logic. |
| app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/AddNewRow2_spec.js | Removed explicit `cy.wait()` calls, relying on Cypress retry for validation error assertions; replaced manual navigation clicks with a helper method. No changes to validation logic. |
| app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/inline_editing_validations_spec.js | Adjusted validation error assertions to expect error presence immediately after editing begins in some cases; removed most explicit waits except one after saving; test titles updated for clarity. |

## Suggested labels

`Widgets Product`, `QA`

## Suggested reviewers

- AmanAgarwal041

## Poem

> 🧮 In tables where the data plays,  
> Validation now walks new ways.  
> Flags aligned and checks refined,  
> Regex split, required defined.  
> Tests grouped tight, waits set free,  
> Clean and clear as code can be!  
> ✨📊

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=appsmithorg/appsmith&utm_content=40679):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: rahulbarwal
created_at: 2025-05-20T06:26:53Z
body: /build-deploy-preview skip-tests=true
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-20T06:27:17Z
body: Deploying Your Preview: <https://github.com/appsmithorg/appsmith/actions/runs/15130429502>.
Workflow: `On demand build Docker image and deploy preview`.
skip-tests: `true`.
env: ``.
PR: 40679.
recreate: .
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-20T06:39:05Z
body: Deploy-Preview-URL: https://ce-40679.dp.appsmith.com
author_association: NONE