diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js index 39906e06f6..4eca007482 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js @@ -2,6 +2,7 @@ const dsl = require("../../../../../fixtures/Table/InlineEditingDSL.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const widgetsPage = require("../../../../../locators/Widgets.json"); import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; +import { PROPERTY_SELECTOR } from "../../../../../locators/WidgetLocators"; const agHelper = ObjectsRegistry.AggregateHelper; describe("Table widget inline editing functionality", () => { @@ -711,4 +712,104 @@ describe("Table widget inline editing functionality", () => { "[data-colindex='0'][data-rowindex='0'] .t--inlined-cell-editor", ).should("not.have.css", "height", "34px"); }); + + it("26. should check if updatedRowIndex is getting updated for single row update mode", () => { + cy.dragAndDropToCanvas("textwidget", { x: 400, y: 400 }); + cy.get(".t--widget-textwidget").should("exist"); + cy.updateCodeInput( + ".t--property-control-text", + `{{Table1.updatedRowIndex}}`, + ); + + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.get(".t--widget-buttonwidget").should("exist"); + cy.get(PROPERTY_SELECTOR.onClick) + .find(".t--js-toggle") + .click(); + cy.updateCodeInput(".t--property-control-label", "Reset"); + cy.updateCodeInput( + PROPERTY_SELECTOR.onClick, + `{{resetWidget("Table1",true)}}`, + ); + + // case 1: check if updatedRowIndex has -1 as the default value: + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + cy.openPropertyPane("tablewidgetv2"); + + cy.makeColumnEditable("step"); + cy.wait(1000); + + // case 2: check if updatedRowIndex is 0, when cell at row 0 is updated. + cy.editTableCell(0, 0); + cy.enterTableCellValue(0, 0, "#12").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 0); + + // case 3: check if updatedRowIndex is -1 when changes are discarded. + cy.discardTableRow(4, 0); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 4: check if the updateRowIndex is -1 when widget is reset + cy.editTableCell(0, 1); + cy.enterTableCellValue(0, 1, "#13").type("{enter}"); + cy.contains("Reset").click({ force: true }); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 5: check if the updatedRowIndex changes to -1 when the table data changes. + cy.wait(1000); + cy.editTableCell(0, 2); + cy.enterTableCellValue(0, 2, "#14").type("{enter}"); + cy.openPropertyPane("tablewidgetv2"); + cy.get(widgetsPage.tabedataField).type("{backspace}"); + cy.wait(300); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + }); + + it.only("27. should check if updatedRowIndex is getting updated for multi row update mode", () => { + cy.dragAndDropToCanvas("textwidget", { x: 400, y: 400 }); + cy.get(".t--widget-textwidget").should("exist"); + cy.updateCodeInput( + ".t--property-control-text", + `{{Table1.updatedRowIndex}}`, + ); + + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.get(".t--widget-buttonwidget").should("exist"); + cy.get(PROPERTY_SELECTOR.onClick) + .find(".t--js-toggle") + .click(); + cy.updateCodeInput(".t--property-control-label", "Reset"); + cy.updateCodeInput( + PROPERTY_SELECTOR.onClick, + `{{resetWidget("Table1",true)}}`, + ); + + cy.openPropertyPane("tablewidgetv2"); + + cy.makeColumnEditable("step"); + cy.get(".t--button-tab-CUSTOM").click({ force: true }); + cy.wait(1000); + + // case 1: check if updatedRowIndex is 0, when cell at row 0 is updated. + cy.editTableCell(0, 0); + cy.enterTableCellValue(0, 0, "#12").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 0); + + // case 2: check if the updateRowIndex is -1 when widget is reset + cy.editTableCell(0, 1); + cy.enterTableCellValue(0, 1, "#13").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 1); + cy.contains("Reset").click({ force: true }); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 3: check if the updatedRowIndex changes to -1 when the table data changes. + cy.wait(1000); + cy.editTableCell(0, 2); + cy.enterTableCellValue(0, 2, "#14").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 2); + cy.openPropertyPane("tablewidgetv2"); + cy.get(widgetsPage.tabedataField).type("{backspace}"); + cy.wait(300); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + }); }); diff --git a/app/client/src/widgets/TableWidgetV2/widget/derived.js b/app/client/src/widgets/TableWidgetV2/widget/derived.js index b6004c5938..d83d00b29d 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/derived.js +++ b/app/client/src/widgets/TableWidgetV2/widget/derived.js @@ -550,6 +550,54 @@ export default { return finalTableData; }, // + getUpdatedRow: (props, moment, _) => { + let index = -1; + const parsedUpdatedRowIndex = parseInt(props.updatedRowIndex); + + if (!_.isNaN(parsedUpdatedRowIndex)) { + index = parsedUpdatedRowIndex; + } + + const rows = props.filteredTableData || props.processedTableData || []; + const primaryColumns = props.primaryColumns; + let updatedRow; + + if (index > -1) { + const row = rows.find((row) => row.__originalIndex__ === index); + updatedRow = { ...row }; + } else { + /* + * If updatedRowIndex is not a valid index, updatedRow should + * have proper row structure with empty string values + */ + updatedRow = {}; + if (rows && rows[0]) { + Object.keys(rows[0]).forEach((key) => { + updatedRow[key] = ""; + }); + } + } + + const nonDataColumnTypes = [ + "editActions", + "button", + "iconButton", + "menuButton", + ]; + const nonDataColumnAliases = primaryColumns + ? Object.values(primaryColumns) + .filter((column) => nonDataColumnTypes.includes(column.columnType)) + .map((column) => column.alias) + : []; + + const keysToBeOmitted = [ + "__originalIndex__", + "__primaryKey__", + ...nonDataColumnAliases, + ]; + return _.omit(updatedRow, keysToBeOmitted); + }, + // getUpdatedRows: (props, moment, _) => { const primaryColumns = props.primaryColumns; const nonDataColumnTypes = [ diff --git a/app/client/src/widgets/TableWidgetV2/widget/derived.test.js b/app/client/src/widgets/TableWidgetV2/widget/derived.test.js index 8fbee5499f..e6fb16e8b0 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/derived.test.js +++ b/app/client/src/widgets/TableWidgetV2/widget/derived.test.js @@ -2069,6 +2069,98 @@ describe("getPageOffset -", () => { }); }); +describe("validate getUpdatedRow", () => { + it("should check that valid updated row index returns the valid value", () => { + const { getUpdatedRow } = derivedProperty; + const input1 = { + updatedRowIndex: 1, + processedTableData: [ + { id: 1234, name: "Jim Doe", extra: "", __originalIndex__: 0 }, + { id: 234, name: "Jane Doe", extra: "Extra2", __originalIndex__: 2 }, + { id: 123, name: "John Doe1", extra: "Extra1", __originalIndex__: 1 }, + ], + }; + const input2 = { + updatedRowIndex: 0, + processedTableData: [ + { id: 1, name: "Lorem Ipsum", extra: "", __originalIndex__: 0 }, + { id: 234, name: "Jane Doe", extra: "Extra2", __originalIndex__: 2 }, + { id: 123, name: "John Doe", extra: "Extra1", __originalIndex__: 1 }, + ], + } + expect(getUpdatedRow(input1, moment, _)).toStrictEqual({ + id: 123, + name: "John Doe1", + extra: "Extra1", + }); + expect(getUpdatedRow(input2, moment, _)).toStrictEqual({ + id: 1, + name: "Lorem Ipsum", + extra: "", + }); + }); + + it("should check that it returns empty values when updateRowIndex is invalid or -1", () => { + const { getUpdatedRow } = derivedProperty; + const input1 = { + updatedRowIndex: -1, + processedTableData: [ + { id: 1, name: "Lorem Ipsum", extra: "", __originalIndex__: 0 }, + { id: 234, name: "Jane Doe", extra: "Extra2", __originalIndex__: 2 }, + { id: 123, name: "John Doe", extra: "Extra1", __originalIndex__: 1 }, + ], + }; + const input2 = { + updatedRowIndex: "dummyIndex", + processedTableData: [ + { id: 1, name: "Lorem Ipsum", extra: "", __originalIndex__: 0 }, + { id: 234, name: "Jane Doe", extra: "Extra2", __originalIndex__: 2 }, + { id: 123, name: "John Doe", extra: "Extra1", __originalIndex__: 1 }, + ], + }; + + const input3 = { + updatedRowIndex: undefined, + processedTableData: [ + { id: 1, name: "Lorem Ipsum", extra: "", __originalIndex__: 0 }, + { id: 234, name: "Jane Doe", extra: "Extra2", __originalIndex__: 2 }, + { id: 123, name: "John Doe", extra: "Extra1", __originalIndex__: 1 }, + ], + }; + expect(getUpdatedRow(input1, moment, _)).toStrictEqual({ + id: "", + name: "", + extra: "", + }); + + expect(getUpdatedRow(input2, moment, _)).toStrictEqual({ + id: "", + name: "", + extra: "", + }); + + expect(getUpdatedRow(input3, moment, _)).toStrictEqual({ + id: "", + name: "", + extra: "", + }); + }); + + it("should check that it removes non data columns", () => { + const { getUpdatedRow } = derivedProperty; + const input = { + updatedRowIndex: 1, + processedTableData: sampleProcessedTableData, + primaryColumns: samplePrimaryColumns, + }; + + expect(getUpdatedRow(input, moment, _)).toStrictEqual({ + step: "#2", + task: "Create a query fetch_users with the Mock DB", + status: "--", + }); + }); +}) describe("getEditableCellValidity", () => { const { getEditableCellValidity } = derivedProperty; diff --git a/app/client/src/widgets/TableWidgetV2/widget/index.tsx b/app/client/src/widgets/TableWidgetV2/widget/index.tsx index ef64706a17..5fd5058d69 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/TableWidgetV2/widget/index.tsx @@ -121,6 +121,7 @@ class TableWidgetV2 extends BaseWidget { order: null, }, transientTableData: {}, + updatedRowIndex: -1, editableCell: defaultEditableCell, columnEditableCellValue: {}, selectColumnFilterText: {}, @@ -142,7 +143,7 @@ class TableWidgetV2 extends BaseWidget { filteredTableData: `{{(()=>{ ${derivedProperties.getFilteredTableData}})()}}`, updatedRows: `{{(()=>{ ${derivedProperties.getUpdatedRows}})()}}`, updatedRowIndices: `{{(()=>{ ${derivedProperties.getUpdatedRowIndices}})()}}`, - updatedRow: `{{this.triggeredRow}}`, + updatedRow: `{{(()=>{ ${derivedProperties.getUpdatedRow}})()}}`, pageOffset: `{{(()=>{${derivedProperties.getPageOffset}})()}}`, isEditableCellsValid: `{{(()=>{ ${derivedProperties.getEditableCellValidity}})()}}`, }; @@ -595,6 +596,9 @@ class TableWidgetV2 extends BaseWidget { */ if (isTableDataModified) { this.props.updateWidgetMetaProperty("transientTableData", {}); + // reset updatedRowIndex whenever transientTableData is flushed. + this.props.updateWidgetMetaProperty("updatedRowIndex", -1); + this.clearEditableCell(true); this.props.updateWidgetMetaProperty("selectColumnFilterText", {}); } @@ -1208,6 +1212,8 @@ class TableWidgetV2 extends BaseWidget { ...transientData, }, }); + + this.props.updateWidgetMetaProperty("updatedRowIndex", __originalIndex__); }; removeRowFromTransientTableData = (index: number) => { @@ -1221,6 +1227,7 @@ class TableWidgetV2 extends BaseWidget { newTransientTableData, ); } + this.props.updateWidgetMetaProperty("updatedRowIndex", -1); }; getRowOriginalIndex = (index: number) => {