diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2Filter1_1_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2Filter1_1_Spec.ts index d5e0741608b..2c529e35fd2 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2Filter1_1_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2Filter1_1_Spec.ts @@ -1,3 +1,5 @@ +import { demoTableDataForSelect } from "../../../../../fixtures/Table/DemoTableData"; +import { featureFlagIntercept } from "../../../../../support/Objects/FeatureFlags"; import { entityExplorer, propPane, @@ -8,6 +10,9 @@ import { draggableWidgets, agHelper, } from "../../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + EntityType, +} from "../../../../../support/Pages/EditorNavigation"; describe( "Verify various Table_Filter combinations", @@ -136,5 +141,46 @@ describe( table.WaitForTableEmpty("v2"); table.RemoveFilterNVerify("2381224", true, true, 0, "v2"); }); + + it("11. Verify table search includes label and value for table with select column type", () => { + // This flag is turned on to allow the label show in the table select cell content + // when this feature is turned on fully, this flag will be removed + featureFlagIntercept({ release_table_cell_label_value_enabled: true }); + deployMode.NavigateBacktoEditor(); + EditorNavigation.SelectEntityByName("Table1", EntityType.Widget); + propPane.EnterJSContext("Table data", demoTableDataForSelect); + + // Edit role column to select type + table.ChangeColumnType("role", "Select", "v2"); + table.EditColumn("role", "v2"); + agHelper.UpdateCodeInput( + locators._controlOption, + ` + {{ + [ + {"label": "Software Engineer", + "value": 10,}, + {"label": "Product Manager", + "value": 20,}, + {"label": "UX Designer", + "value": 30,} + ] + }} + `, + ); + // Search for a label in the table + table.SearchTable("Software Engineer"); + table.ReadTableRowColumnData(0, 2, "v2").then((afterSearch) => { + expect(afterSearch).to.eq("Software Engineer"); + }); + table.RemoveSearchTextNVerify("1", "v2"); + + // Search for a value in the table + table.SearchTable("20"); + table.ReadTableRowColumnData(0, 2, "v2").then((afterSearch) => { + expect(afterSearch).to.eq("Product Manager"); + }); + table.RemoveSearchTextNVerify("1", "v2"); + }); }, ); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/columnTypes/Select1_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/columnTypes/Select1_spec.ts index f6593370866..dd77c5338ad 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/columnTypes/Select1_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/columnTypes/Select1_spec.ts @@ -37,10 +37,10 @@ describe( }); }); - it("3. should check that options given in the property pane is appearing on the table", () => { - cy.get(".t--property-control-options").should("exist"); + it("3. should check that JSON options given in the property pane is appearing on the table", () => { + cy.get(_.locators._controlOption).should("exist"); cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, ` [ { @@ -80,9 +80,52 @@ describe( cy.get(".menu-item-active.has-focus").should("contain", "#1"); }); - it("4. should check that placeholder property is working", () => { + it("4. should check that javascript options given in the property pane is appearing on the table", () => { + cy.get(_.locators._controlOption).should("exist"); cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, + ` + {{[ + { + "label": "#1", + "value": "#1" + }, + { + "label": "#2", + "value": "#2" + }, + { + "label": "#3", + "value": "#3" + } + ]}} + `, + ); + cy.editTableSelectCell(0, 0); + + [ + { + label: "#1", + value: "#1", + }, + { + label: "#2", + value: "#2", + }, + { + label: "#3", + value: "#3", + }, + ].forEach((item) => { + cy.get(".menu-item-text").contains(item.value).should("exist"); + }); + + cy.get(".menu-item-active.has-focus").should("contain", "#1"); + }); + + it("5. should check that placeholder property is working", () => { + cy.updateCodeInput( + _.locators._controlOption, ` [ { @@ -116,9 +159,9 @@ describe( ).should("contain", "choose an item"); }); - it("5. should check that filterable property is working", () => { + it("6. should check that filterable property is working", () => { cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, ` {{[ { @@ -161,7 +204,7 @@ describe( cy.get(".t--canvas-artboard").click({ force: true }); }); - it("6. should check that on option select is working", () => { + it("7. should check that on option select is working", () => { _.agHelper.CheckForPageSaveError(); featureFlagIntercept({ release_table_cell_label_value_enabled: true }); cy.openPropertyPane("tablewidgetv2"); @@ -174,7 +217,7 @@ describe( `, ); cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, ` [ { @@ -204,9 +247,9 @@ describe( cy.discardTableRow(4, 0); }); - it("7. should check that currentRow is accessible in the select options", () => { + it("8. should check that currentRow is accessible in the select options", () => { cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, ` {{[ { @@ -229,7 +272,7 @@ describe( cy.get(".menu-item-text").contains("#1").should("not.exist"); }); - it("8. should check that 'same select option in new row' property is working", () => { + it("9. should check that 'same select option in new row' property is working", () => { _.propPane.NavigateBackToPropertyPane(); const checkSameOptionsInNewRowWhileEditing = () => { @@ -246,7 +289,7 @@ describe( cy.get(".t--property-control-newrowoptions").should("not.exist"); cy.updateCodeInput( - ".t--property-control-options", + _.locators._controlOption, ` {{[{ "label": "male", @@ -295,7 +338,7 @@ describe( checkSameOptionsWhileAddingNewRow(); }); - it("9. should check that 'new row select options' is working", () => { + it("10. should check that 'new row select options' is working", () => { const checkNewRowOptions = () => { // New row select options should be visible when "Same options in new row" is turned off _.propPane.TogglePropertyState("Same options in new row", "Off"); @@ -360,7 +403,7 @@ describe( checkNoOptionState(); }); - it("10. should check that server side filering is working", () => { + it("11. should check that server side filering is working", () => { _.dataSources.CreateDataSource("Postgres"); _.dataSources.CreateQueryAfterDSSaved( "SELECT * FROM public.astronauts {{this.params.filterText ? `WHERE name LIKE '%${this.params.filterText}%'` : ''}} LIMIT 10;", diff --git a/app/client/cypress/fixtures/Table/DemoTableData.ts b/app/client/cypress/fixtures/Table/DemoTableData.ts new file mode 100644 index 00000000000..1b0bfdfb49e --- /dev/null +++ b/app/client/cypress/fixtures/Table/DemoTableData.ts @@ -0,0 +1,46 @@ +export const demoTableDataForSelect = ` +{{ + [ + { + role: 10, + id: 1, + name: "Alice Johnson", + email: "alice.johnson@example.com", + age: 28, + gender: 2 + }, + { + role: 20, + id: 2, + name: "Bob Smith", + email: "bob.smith@example.com", + age: 34, + gender: 1 + }, + { + role: 30, + id: 3, + name: "Charlie Brown", + email: "charlie.brown@example.com", + age: 25, + gender: 3 + }, + { + role: 20, + id: 4, + name: "Diana Prince", + email: "diana.prince@example.com", + age: 30, + gender: 2 + }, + { + role: 10, + id: 5, + name: "Evan Williams", + email: "evan.williams@example.com", + age: 27, + gender: 1 + } + ] +}} + `; diff --git a/app/client/src/widgets/TableWidgetV2/widget/derived.js b/app/client/src/widgets/TableWidgetV2/widget/derived.js index ad69270fa97..aa5873dd548 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/derived.js +++ b/app/client/src/widgets/TableWidgetV2/widget/derived.js @@ -342,12 +342,46 @@ export default { const newRow = { ...row }; selectColumnKeysWithSortByLabel.forEach((key) => { const value = row[key]; - const selectOptions = - primaryColumns[key].selectOptions[row.__originalIndex__]; - const option = selectOptions.find((option) => option.value === value); + const isSelectOptionsAnArray = _.isArray( + primaryColumns[key].selectOptions, + ); + + let selectOptions; - if (option) { - newRow[key] = option.label; + /* + * If selectOptions is an array, check if it contains nested arrays. + * This is to handle situations where selectOptons is a javascript object and computes as a nested array. + */ + if (isSelectOptionsAnArray) { + if (_.some(primaryColumns[key].selectOptions, _.isArray)) { + /* Handle the case where selectOptions contains nested arrays - selectOptions is javascript */ + selectOptions = + primaryColumns[key].selectOptions[row.__originalIndex__]; + const option = selectOptions.find((option) => { + return option.value === value; + }); + if (option) { + newRow[key] = option.label; + } + } else { + /* Handle the case where selectOptions is a flat array - selectOptions is plain JSON */ + selectOptions = primaryColumns[key].selectOptions; + const option = selectOptions.find( + (option) => option.value === value, + ); + if (option) { + newRow[key] = option.label; + } + } + } else { + /* If selectOptions is not an array, parse it as JSON - not evaluated yet, so returns as string */ + selectOptions = JSON.parse(primaryColumns[key].selectOptions); + const option = selectOptions.find( + (option) => option.value === value, + ); + if (option) { + newRow[key] = option.label; + } } }); @@ -450,18 +484,55 @@ export default { } }); + /* + * When sorting is done, transform the data back to its original state + * where table data shows value instead of label + */ if (selectColumnKeysWithSortByLabel.length) { const transformedLabelToValueData = sortedTableData.map((row) => { const newRow = { ...row }; selectColumnKeysWithSortByLabel.forEach((key) => { const label = row[key]; - const selectOptions = - primaryColumns[key].selectOptions[row.__originalIndex__]; - const option = selectOptions.find( - (option) => option.label === label, + const isSelectOptionsAnArray = _.isArray( + primaryColumns[key].selectOptions, ); - if (option) { - newRow[key] = option.value; + + let selectOptions; + + /* + * If selectOptions is an array, check if it contains nested arrays. + * This is to handle situations where selectOptons is a javascript object and computes as a nested array. + */ + if (isSelectOptionsAnArray) { + if (_.some(primaryColumns[key].selectOptions, _.isArray)) { + /* Handle the case where selectOptions contains nested arrays - selectOptions is javascript */ + selectOptions = + primaryColumns[key].selectOptions[row.__originalIndex__]; + const option = selectOptions.find((option) => { + return option.label === label; + }); + if (option) { + newRow[key] = option.value; + } + } else { + /* Handle the case where selectOptions is a flat array - selectOptions is plain JSON */ + selectOptions = primaryColumns[key].selectOptions; + const option = selectOptions.find( + (option) => option.label === label, + ); + if (option) { + newRow[key] = option.value; + } + } + } else { + /* If selectOptions is not an array, parse it as JSON - not evaluated yet, so returns as string */ + selectOptions = JSON.parse(primaryColumns[key].selectOptions); + const option = selectOptions.find( + (option) => option.label === label, + ); + if (option) { + newRow[key] = option.value; + } } }); @@ -587,8 +658,83 @@ export default { const columnWithDisplayText = Object.values(props.primaryColumns).filter( (column) => column.columnType === "url" && column.displayText, ); + + /* + * For select columns with label and values, we need to include the label value + * in the search + */ + let labelValueForSelectCell = ""; + /* + * Initialize an array to store keys for columns that have the 'select' column type + * and contain selectOptions. + */ + const selectColumnKeys = []; + /* + * Iterate over the primary columns to identify which columns are of type 'select' + * and have selectOptions. These keys are pushed into the selectColumnKeys array. + */ + Object.entries(props.primaryColumns).forEach(([id, column]) => { + const isColumnSelectColumnType = + column?.columnType === "select" && column?.selectOptions?.length; + if (isColumnSelectColumnType) { + selectColumnKeys.push(id); + } + }); + /* + * If there are any select columns, iterate over them to find the label value + * associated with the selected value in each row. + */ + if (selectColumnKeys.length) { + selectColumnKeys.forEach((key) => { + const value = row[key]; + + const isSelectOptionsAnArray = _.isArray( + primaryColumns[key].selectOptions, + ); + + let selectOptions; + + /* + * If selectOptions is an array, check if it contains nested arrays. + * This is to handle situations where selectOptons is a javascript object and computes as a nested array. + */ + if (isSelectOptionsAnArray) { + if (_.some(primaryColumns[key].selectOptions, _.isArray)) { + /* Handle the case where selectOptions contains nested arrays - selectOptions is javascript */ + selectOptions = + primaryColumns[key].selectOptions[row.__originalIndex__]; + const option = selectOptions.find((option) => { + return option.value === value; + }); + if (option) { + labelValueForSelectCell = option.label; + } + } else { + /* Handle the case where selectOptions is a flat array - selectOptions is plain JSON */ + selectOptions = primaryColumns[key].selectOptions; + const option = selectOptions.find( + (option) => option.value === value, + ); + if (option) { + labelValueForSelectCell = option.label; + } + } + } else { + /* If selectOptions is not an array, parse it as JSON - not evaluated yet, so returns as string */ + selectOptions = JSON.parse(primaryColumns[key].selectOptions); + const option = selectOptions.find( + (option) => option.value === value, + ); + if (option) { + labelValueForSelectCell = option.label; + } + } + }); + } + const displayedRow = { ...row, + labelValueForSelectCell, ...columnWithDisplayText.reduce((acc, column) => { let displayText; if (_.isArray(column.displayText)) {