import React, { lazy, Suspense } from "react"; import BaseWidget, { WidgetState } from "../BaseWidget"; import { RenderModes, WidgetType } from "constants/WidgetConstants"; import { EventType } from "constants/ActionConstants"; import { compare, getDefaultColumnProperties, getTableStyles, renderCell, renderDropdown, renderActions, sortTableFunction, reorderColumns, } from "components/designSystems/appsmith/TableComponent/TableUtilities"; import { getAllTableColumnKeys } from "components/designSystems/appsmith/TableComponent/TableHelpers"; import { VALIDATION_TYPES } from "constants/WidgetValidation"; import { BASE_WIDGET_VALIDATION, WidgetPropertyValidationType, } from "utils/WidgetValidation"; import { TriggerPropertiesMap } from "utils/WidgetFactory"; import Skeleton from "components/utils/Skeleton"; import moment from "moment"; import { isNumber, isString, isUndefined, isEqual, xor, without } from "lodash"; import * as Sentry from "@sentry/react"; import { retryPromise } from "utils/AppsmithUtils"; import withMeta from "../MetaHOC"; import { getDynamicBindings } from "utils/DynamicBindingUtils"; import log from "loglevel"; import { ReactTableFilter } from "components/designSystems/appsmith/TableComponent/TableFilters"; import { TableWidgetProps } from "./TableWidgetConstants"; import derivedProperties from "./parseDerivedProperties"; import { ColumnProperties, CellLayoutProperties, ReactTableColumnProps, ColumnTypes, Operator, OperatorTypes, CompactModeTypes, CompactMode, } from "components/designSystems/appsmith/TableComponent/Constants"; import tablePropertyPaneConfig from "./TablePropertyPaneConfig"; const ReactTableComponent = lazy(() => retryPromise(() => import("components/designSystems/appsmith/TableComponent"), ), ); class TableWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { return { ...BASE_WIDGET_VALIDATION, tableData: VALIDATION_TYPES.TABLE_DATA, nextPageKey: VALIDATION_TYPES.TEXT, prevPageKey: VALIDATION_TYPES.TEXT, label: VALIDATION_TYPES.TEXT, searchText: VALIDATION_TYPES.TEXT, defaultSearchText: VALIDATION_TYPES.TEXT, defaultSelectedRow: VALIDATION_TYPES.DEFAULT_SELECTED_ROW, pageSize: VALIDATION_TYPES.NUMBER, }; } static getPropertyPaneConfig() { return tablePropertyPaneConfig; } static getMetaPropertiesMap(): Record { return { pageNo: 1, selectedRowIndex: undefined, selectedRowIndices: undefined, searchText: undefined, // The following meta property is used for rendering the table. filteredTableData: undefined, filters: [], hiddenColumns: [], compactMode: CompactModeTypes.DEFAULT, }; } static getDerivedPropertiesMap() { return { selectedRow: `{{(()=>{${derivedProperties.getSelectedRow}})()}}`, selectedRows: `{{(()=>{${derivedProperties.getSelectedRows}})()}}`, pageSize: `{{(()=>{${derivedProperties.getPageSize}})()}}`, triggerRowSelection: "{{!!this.onRowSelected}}", }; } static getDefaultPropertiesMap(): Record { return { searchText: "defaultSearchText", selectedRowIndex: "defaultSelectedRow", selectedRowIndices: "defaultSelectedRow", }; } static getTriggerPropertyMap(): TriggerPropertiesMap { return { onRowSelected: true, onPageChange: true, onSearchTextChanged: true, onPageSizeChange: true, }; } getPropertyValue = (value: any, index: number, preserveCase = false) => { if (value && Array.isArray(value) && value[index]) { return preserveCase ? value[index].toString() : value[index].toString().toUpperCase(); } else if (value) { return preserveCase ? value.toString() : value.toString().toUpperCase(); } else { return value; } }; getCellProperties = ( columnProperties: ColumnProperties, rowIndex: number, ) => { const cellProperties: CellLayoutProperties = { horizontalAlignment: this.getPropertyValue( columnProperties.horizontalAlignment, rowIndex, ), verticalAlignment: this.getPropertyValue( columnProperties.verticalAlignment, rowIndex, ), cellBackground: this.getPropertyValue( columnProperties.cellBackground, rowIndex, ), buttonStyle: this.getPropertyValue( columnProperties.buttonStyle, rowIndex, ), buttonLabelColor: this.getPropertyValue( columnProperties.buttonLabelColor, rowIndex, ), buttonLabel: this.getPropertyValue( columnProperties.buttonLabel, rowIndex, true, ), textSize: this.getPropertyValue(columnProperties.textSize, rowIndex), textColor: this.getPropertyValue(columnProperties.textColor, rowIndex), fontStyle: this.getPropertyValue(columnProperties.fontStyle, rowIndex), //Fix this }; return cellProperties; }; getTableColumns = () => { let columns: ReactTableColumnProps[] = []; const hiddenColumns: ReactTableColumnProps[] = []; const { sortedColumn, columnOrder, columnSizeMap, primaryColumns = {}, } = this.props; let allColumns = Object.assign( {}, this.createTablePrimaryColumns() || primaryColumns, ); const sortColumn = sortedColumn?.column; const sortOrder = sortedColumn?.asc; if (columnOrder) { allColumns = reorderColumns(allColumns, columnOrder); } const { componentWidth } = this.getComponentDimensions(); let totalColumnSizes = 0; const defaultColumnWidth = 150; for (const i in columnSizeMap) { totalColumnSizes += columnSizeMap[i]; } const allColumnProperties = Object.values(allColumns); for (let index = 0; index < allColumnProperties.length; index++) { const columnProperties = allColumnProperties[index]; const isHidden = !columnProperties.isVisible; const accessor = columnProperties.id; const columnData = { Header: columnProperties.label, accessor: accessor, width: columnSizeMap && columnSizeMap[accessor] ? columnSizeMap[accessor] : defaultColumnWidth, minWidth: 60, draggable: true, isHidden: false, isAscOrder: columnProperties.id === sortColumn ? sortOrder : undefined, isDerived: columnProperties.isDerived, metaProperties: { isHidden: isHidden, type: columnProperties.columnType, format: columnProperties?.outputFormat || "", inputFormat: columnProperties?.inputFormat || "", }, columnProperties: columnProperties, Cell: (props: any) => { const rowIndex: number = props.cell.row.index; const cellProperties = this.getCellProperties( columnProperties, rowIndex, ); if (columnProperties.columnType === "button") { const buttonProps = { isSelected: !!props.row.isSelected, onCommandClick: (action: string, onComplete: () => void) => this.onCommandClick(rowIndex, action, onComplete), backgroundColor: cellProperties.buttonStyle || "#29CCA3", buttonLabelColor: cellProperties.buttonLabelColor || "#FFFFFF", columnActions: [ { id: columnProperties.id, label: cellProperties.buttonLabel || "Action", dynamicTrigger: columnProperties.onClick || "", }, ], }; return renderActions(buttonProps, isHidden, cellProperties); } else if (columnProperties.columnType === "dropdown") { let options = []; try { options = JSON.parse(columnProperties.dropdownOptions || ""); } catch (e) {} return renderDropdown({ options: options, onItemSelect: this.onItemSelect, onOptionChange: columnProperties.onOptionChange || "", selectedIndex: isNumber(props.cell.value) ? props.cell.value : undefined, }); } else { return renderCell( props.cell.value, columnProperties.columnType, isHidden, cellProperties, componentWidth, ); } }, }; if (isHidden) { columnData.isHidden = true; hiddenColumns.push(columnData); } else { columns.push(columnData); } } if (totalColumnSizes < componentWidth) { const lastColumnIndex = columns.length - 1; let remainingColumnsSize = 0; for (let i = 0; i < columns.length - 1; i++) { remainingColumnsSize += columns[i].width || defaultColumnWidth; } if (columns[lastColumnIndex]) { columns[lastColumnIndex].width = componentWidth - remainingColumnsSize < defaultColumnWidth ? defaultColumnWidth : componentWidth - remainingColumnsSize; //Min remaining width to be defaultColumnWidth } } if (hiddenColumns.length && this.props.renderMode === RenderModes.CANVAS) { columns = columns.concat(hiddenColumns); } return columns.filter((column: ReactTableColumnProps) => column.accessor); }; transformData = ( tableData: Array>, columns: ReactTableColumnProps[], ) => { const updatedTableData = []; // For each row in the tableData (filteredTableData) for (let row = 0; row < tableData.length; row++) { // Get the row object const data: { [key: string]: any } = tableData[row]; if (data) { const tableRow: { [key: string]: any } = {}; // For each column in the expected columns of the table for (let colIndex = 0; colIndex < columns.length; colIndex++) { // Get the column properties const column = columns[colIndex]; const { accessor } = column; let value = data[accessor]; if (column.metaProperties) { const type = column.metaProperties.type; switch (type) { case ColumnTypes.DATE: let isValidDate = true; let outputFormat = column.metaProperties.format; let inputFormat; try { const type = column.metaProperties.inputFormat; if (type !== "Epoch" && type !== "Milliseconds") { inputFormat = type; moment(value, inputFormat); } else if (!isNumber(value)) { isValidDate = false; } } catch (e) { isValidDate = false; } if (isValidDate) { if (outputFormat === "SAME_AS_INPUT") { outputFormat = inputFormat; } if (column.metaProperties.inputFormat === "Milliseconds") { value = Number(value); } else if (column.metaProperties.inputFormat === "Epoch") { value = 1000 * Number(value); } tableRow[accessor] = moment(value, inputFormat).format( outputFormat, ); } else if (value) { tableRow[accessor] = "Invalid Value"; } else { tableRow[accessor] = ""; } break; default: const data = isString(value) || isNumber(value) ? value : isUndefined(value) ? "" : JSON.stringify(value); tableRow[accessor] = data; break; } } } updatedTableData.push(tableRow); } } return updatedTableData; }; getParsedComputedValues = (value: string | Array) => { let computedValues: Array = []; if (isString(value)) { try { computedValues = JSON.parse(value); } catch (e) { log.debug("Error parsing column value: ", value); } } else if (Array.isArray(value)) { computedValues = value; } else { log.debug("Error parsing column values:", value); } return computedValues; }; filterTableData = () => { const { searchText, sortedColumn, filters, tableData, derivedColumns, } = this.props; if (!tableData || !tableData.length) { return []; } const derivedTableData: Array> = [...tableData]; // If we've already computed the columns list if (this.props.primaryColumns) { const primaryColumns = this.props.primaryColumns; const columnIds = Object.keys(this.props.primaryColumns); // For each column in the table columnIds.forEach((columnId: string) => { // Get the column properties const column: ColumnProperties = primaryColumns[columnId]; let computedValues: Array = []; if (column && column.computedValue) { computedValues = this.getParsedComputedValues(column.computedValue); } if (computedValues.length === 0) { if (derivedColumns) { // Find the derived column with the same column id as the current column const derivedColumn = derivedColumns[columnId]; // if such a derived column exists, use it. if (derivedColumn) { computedValues = this.getParsedComputedValues( derivedColumn.computedValue, ); } } } // Fill the values from the computed values into the table data. for (let index = 0; index < computedValues.length; index++) { derivedTableData[index] = { ...derivedTableData[index], [columnId]: computedValues[index], }; } }); } let sortedTableData: any[]; const columns = this.getTableColumns() || []; const searchKey = searchText ? searchText.toUpperCase() : ""; if (sortedColumn) { const sortColumn = sortedColumn.column; const sortOrder = sortedColumn.asc; sortedTableData = sortTableFunction( derivedTableData, columns, sortColumn, sortOrder, ); } else { sortedTableData = [...derivedTableData]; } const finalTableData = sortedTableData.filter( (item: { [key: string]: any }) => { const searchFound = searchKey ? Object.values(item) .join(", ") .toUpperCase() .includes(searchKey) : true; if (!searchFound) return false; if (!filters || filters.length === 0) return true; const filterOperator: Operator = filters.length >= 2 ? filters[1].operator : OperatorTypes.OR; let filter = filterOperator === OperatorTypes.AND; for (let i = 0; i < filters.length; i++) { const filterValue = compare( item[filters[i].column], filters[i].value, filters[i].condition, ); if (filterOperator === OperatorTypes.AND) { filter = filter && filterValue; } else { filter = filter || filterValue; } } return filter; }, ); return finalTableData; }; getEmptyRow = () => { const columnKeys: string[] = getAllTableColumnKeys(this.props.tableData); const selectedRow: { [key: string]: any } = {}; for (let i = 0; i < columnKeys.length; i++) { selectedRow[columnKeys[i]] = ""; } return selectedRow; }; getSelectedRow = ( filteredTableData: Array>, selectedRowIndex?: number, ) => { if ( selectedRowIndex === undefined || selectedRowIndex === -1 || selectedRowIndex === null ) { return this.getEmptyRow(); } return { ...filteredTableData[selectedRowIndex], }; }; getDerivedColumns = ( derivedColumns: Record, tableColumnCount: number, ) => { if (!derivedColumns) return []; //update index property of all columns in new derived columns return ( Object.keys(derivedColumns)?.map((columnId: string, index: number) => { return { ...derivedColumns[columnId], index: index + tableColumnCount, }; }) || [] ); }; createTablePrimaryColumns = (): | Record | undefined => { const { tableData, primaryColumns = {}, columnNameMap = {}, columnTypeMap = {}, derivedColumns = {}, migrated, } = this.props; const previousColumnIds = Object.keys(primaryColumns); const tableColumns: Record = {}; //Get table level styles const tableStyles = getTableStyles(this.props); const columnKeys: string[] = getAllTableColumnKeys(tableData); // Generate default column properties for all columns // But donot replace existing columns with the same id for (let index = 0; index < columnKeys.length; index++) { const i = columnKeys[index]; const prevIndex = previousColumnIds.indexOf(i); if (prevIndex > -1) { // we found an existing property with the same column id use the previous properties tableColumns[i] = primaryColumns[i]; } else { const columnProperties = getDefaultColumnProperties( i, index, this.props.widgetName, ); if (migrated === false) { if ((columnNameMap as Record)[i]) { columnProperties.label = columnNameMap[i]; } if ( (columnTypeMap as Record< string, { type: ColumnTypes; inputFormat?: string; format?: string } >)[i] ) { columnProperties.columnType = columnTypeMap[i].type; columnProperties.inputFormat = columnTypeMap[i].inputFormat; columnProperties.outputFormat = columnTypeMap[i].format; } } //add column properties along with table level styles tableColumns[columnProperties.id] = { ...columnProperties, ...tableStyles, }; } } // Get derived columns const updatedDerivedColumns = this.getDerivedColumns( derivedColumns, Object.keys(tableColumns).length, ); //add derived columns to primary columns updatedDerivedColumns.forEach((derivedColumn: ColumnProperties) => { tableColumns[derivedColumn.id] = derivedColumn; }); const newColumnIds = Object.keys(tableColumns); if (xor(previousColumnIds, newColumnIds).length > 0) return tableColumns; else return; }; updateColumnProperties = ( tableColumns?: Record, ) => { const { primaryColumns = {} } = this.props; const { columnOrder, migrated } = this.props; if (tableColumns) { const previousColumnIds = Object.keys(primaryColumns); const newColumnIds = Object.keys(tableColumns); if (xor(previousColumnIds, newColumnIds).length > 0) { const columnIdsToAdd = without(newColumnIds, ...previousColumnIds); const propertiesToAdd: Record = {}; columnIdsToAdd.forEach((id: string) => { Object.entries(tableColumns[id]).forEach(([key, value]) => { propertiesToAdd[`primaryColumns.${id}.${key}`] = value; }); }); // If new columnOrders have different values from the original columnOrders if (xor(newColumnIds, columnOrder).length > 0) { propertiesToAdd["columnOrder"] = newColumnIds; } const pathsToDelete: string[] = []; if (migrated === false) { propertiesToAdd["migrated"] = true; } super.batchUpdateWidgetProperty(propertiesToAdd); if (previousColumnIds.length > newColumnIds.length) { const columnsIdsToDelete = without( previousColumnIds, ...newColumnIds, ); columnsIdsToDelete.forEach((id: string) => { pathsToDelete.push(`primaryColumns.${id}`); }); // We need to wait for the above updates to finish // Todo(abhinav): This is not correct. The platform should accept multiple types of updates // That approach should be performant. setTimeout(() => { super.deleteWidgetProperty(pathsToDelete); }, 1000); } } } }; componentDidMount() { const { tableData } = this.props; let newPrimaryColumns; // When we have tableData, the primaryColumns order is unlikely to change // When we don't have tableData primaryColumns will not be available, so let's let it be. if (tableData.length > 0) { newPrimaryColumns = this.createTablePrimaryColumns(); } if (!newPrimaryColumns) { const filteredTableData = this.filterTableData(); this.props.updateWidgetMetaProperty( "filteredTableData", filteredTableData, ); } else { this.updateColumnProperties(newPrimaryColumns); } } componentDidUpdate(prevProps: TableWidgetProps) { const { primaryColumns = {} } = this.props; // Check if data is modifed by comparing the stringified versions of the previous and next tableData const tableDataModified = JSON.stringify(this.props.tableData) !== JSON.stringify(prevProps.tableData); // let hasPrimaryColumnsComputedValueChanged = false; // const oldComputedValues = Object.values( // prevProps.primaryColumns || {}, // )?.map((column: ColumnProperties) => column.computedValue); // const newComputedValues = Object.values( // this.props.primaryColumns || {}, // )?.map((column: ColumnProperties) => column.computedValue); // if (!isEqual(oldComputedValues, newComputedValues)) { // hasPrimaryColumnsComputedValueChanged = true; // } let hasPrimaryColumnsChanged = false; // If the user has changed the tableData OR // The binding has returned a new value if (tableDataModified) { // Get columns keys from this.props.tableData const columnIds: string[] = getAllTableColumnKeys(this.props.tableData); // Get column keys from columns except for derivedColumns const primaryColumnIds = Object.keys(primaryColumns).filter( (id: string) => { return !primaryColumns[id].isDerived; // Filter out the derived columns }, ); // If the keys which exist in the tableData are different from the ones available in primaryColumns if (xor(columnIds, primaryColumnIds).length > 0) { const newTableColumns = this.createTablePrimaryColumns(); // This updates the widget hasPrimaryColumnsChanged = !!newTableColumns; this.updateColumnProperties(newTableColumns); } } // If tableData has changed or // Table filters have changed or // Table search Text has changed or // Sorting has changed // filteredTableData is not created if ( !hasPrimaryColumnsChanged && (JSON.stringify(this.props.filters) !== JSON.stringify(prevProps.filters) || this.props.searchText !== prevProps.searchText || JSON.stringify(this.props.sortedColumn) !== JSON.stringify(prevProps.sortedColumn) || JSON.stringify(this.props.primaryColumns) !== JSON.stringify(prevProps.primaryColumns) || this.props.filteredTableData === undefined) ) { if (this.props.primaryColumns && Object.keys(primaryColumns).length > 0) { const filteredTableData = this.filterTableData(); if ( JSON.stringify(filteredTableData) !== JSON.stringify(this.props.filteredTableData) ) { // Update filteredTableData meta property this.props.updateWidgetMetaProperty( "filteredTableData", filteredTableData, ); } } } // If the user has switched the mutiple row selection feature if (this.props.multiRowSelection !== prevProps.multiRowSelection) { // It is switched ON: if (this.props.multiRowSelection) { // Use the selectedRowIndex if available as default selected index const selectedRowIndices = this.props.selectedRowIndex ? [this.props.selectedRowIndex] : []; // Else use an empty array this.props.updateWidgetMetaProperty( "selectedRowIndices", selectedRowIndices, ); this.props.updateWidgetMetaProperty("selectedRowIndex", -1); } else { this.props.updateWidgetMetaProperty("selectedRowIndices", []); } } // If the user changed the defaultSelectedRow(s) if (!isEqual(this.props.defaultSelectedRow, prevProps.defaultSelectedRow)) { //Runs only when defaultSelectedRow is changed from property pane if (!this.props.multiRowSelection) { const selectedRowIndex = isNumber(this.props.defaultSelectedRow) ? this.props.defaultSelectedRow : -1; this.props.updateWidgetMetaProperty( "selectedRowIndex", selectedRowIndex, ); } else { const selectedRowIndices = Array.isArray(this.props.defaultSelectedRow) ? this.props.defaultSelectedRow : []; this.props.updateWidgetMetaProperty( "selectedRowIndices", selectedRowIndices, ); } } if (this.props.pageSize !== prevProps.pageSize) { super.executeAction({ dynamicString: this.props.onPageSizeChange, event: { type: EventType.ON_PAGE_SIZE_CHANGE, }, }); } } getSelectedRowIndexes = (selectedRowIndices: string) => { return selectedRowIndices ? selectedRowIndices.split(",").map((i) => Number(i)) : []; }; getPageView() { const { hiddenColumns, pageSize } = this.props; const filteredTableData = this.filterTableData(); const computedSelectedRowIndices = Array.isArray( this.props.selectedRowIndices, ) ? this.props.selectedRowIndices : []; const tableColumns = this.getTableColumns() || []; const transformedData = this.transformData( filteredTableData || [], tableColumns, ); const serverSidePaginationEnabled = (this.props .serverSidePaginationEnabled && this.props.serverSidePaginationEnabled) as boolean; let pageNo = this.props.pageNo; if (pageNo === undefined) { pageNo = 1; this.props.updateWidgetMetaProperty("pageNo", pageNo); } const { componentWidth, componentHeight } = this.getComponentDimensions(); return ( }> { super.updateWidgetProperty("hiddenColumns", hiddenColumns); }} handleReorderColumn={this.handleReorderColumn} disableDrag={(disable: boolean) => { this.disableDrag(disable); }} searchTableData={this.handleSearchTable} filters={this.props.filters} applyFilter={(filters: ReactTableFilter[]) => { this.resetSelectedRowIndex(); this.props.updateWidgetMetaProperty("filters", filters); }} compactMode={this.props.compactMode || CompactModeTypes.DEFAULT} updateCompactMode={(compactMode: CompactMode) => { this.props.updateWidgetMetaProperty("compactMode", compactMode); }} sortTableColumn={this.handleColumnSorting} /> ); } handleReorderColumn = (columnOrder: string[]) => { if (this.props.renderMode === RenderModes.CANVAS) { super.updateWidgetProperty("columnOrder", columnOrder); } else this.props.updateWidgetMetaProperty("columnOrder", columnOrder); }; handleColumnSorting = (column: string, asc: boolean) => { this.resetSelectedRowIndex(); if (column === "") { this.props.updateWidgetMetaProperty("sortedColumn", undefined); } else { this.props.updateWidgetMetaProperty("sortedColumn", { column: column, asc: asc, }); } }; handleResizeColumn = (columnSizeMap: { [key: string]: number }) => { if (this.props.renderMode === RenderModes.CANVAS) { super.updateWidgetProperty("columnSizeMap", columnSizeMap); } else { this.props.updateWidgetMetaProperty("columnSizeMap", columnSizeMap); } }; handleSearchTable = (searchKey: any) => { const { onSearchTextChanged } = this.props; this.resetSelectedRowIndex(); this.props.updateWidgetMetaProperty("pageNo", 1); this.props.updateWidgetMetaProperty("searchText", searchKey, { dynamicString: onSearchTextChanged, event: { type: EventType.ON_SEARCH, }, }); }; updateHiddenColumns = (hiddenColumns?: string[]) => { super.updateWidgetProperty("hiddenColumns", hiddenColumns); }; onCommandClick = ( rowIndex: number, action: string, onComplete: () => void, ) => { try { const rowData = [this.props.filteredTableData[rowIndex]]; const { jsSnippets } = getDynamicBindings(action); const modifiedAction = jsSnippets.reduce((prev: string, next: string) => { return prev + `{{(currentRow) => { ${next} }}} `; }, ""); super.executeAction({ dynamicString: modifiedAction, event: { type: EventType.ON_CLICK, callback: onComplete, }, responseData: rowData, }); } catch (error) { log.debug("Error parsing row action", error); } }; onItemSelect = (action: string) => { super.executeAction({ dynamicString: action, event: { type: EventType.ON_OPTION_CHANGE, }, }); }; handleRowClick = (rowData: Record, index: number) => { if (this.props.multiRowSelection) { const selectedRowIndices = this.props.selectedRowIndices ? [...this.props.selectedRowIndices] : []; if (selectedRowIndices.includes(index)) { const rowIndex = selectedRowIndices.indexOf(index); selectedRowIndices.splice(rowIndex, 1); } else { selectedRowIndices.push(index); } this.props.updateWidgetMetaProperty( "selectedRowIndices", selectedRowIndices, ); this.props.updateWidgetMetaProperty( "selectedRows", this.props.filteredTableData.filter( (item: Record, i: number) => { return selectedRowIndices.includes(i); }, ), ); } else { const selectedRowIndex = isNumber(this.props.selectedRowIndex) ? this.props.selectedRowIndex : -1; if (selectedRowIndex === index) { index = -1; } else { this.props.updateWidgetMetaProperty( "selectedRow", this.props.filteredTableData[index], { dynamicString: this.props.onRowSelected, event: { type: EventType.ON_ROW_SELECTED, }, }, ); } this.props.updateWidgetMetaProperty("selectedRowIndex", index); } }; updatePageNumber = (pageNo: number, event?: EventType) => { if (event) { this.props.updateWidgetMetaProperty("pageNo", pageNo, { dynamicString: this.props.onPageChange, event: { type: event, }, }); } else { this.props.updateWidgetMetaProperty("pageNo", pageNo); } if (this.props.onPageChange) { this.resetSelectedRowIndex(); } }; handleNextPageClick = () => { let pageNo = this.props.pageNo || 1; pageNo = pageNo + 1; this.props.updateWidgetMetaProperty("pageNo", pageNo, { dynamicString: this.props.onPageChange, event: { type: EventType.ON_NEXT_PAGE, }, }); if (this.props.onPageChange) { this.resetSelectedRowIndex(); } }; resetSelectedRowIndex = () => { if (!this.props.multiRowSelection) { const selectedRowIndex = isNumber(this.props.defaultSelectedRow) ? this.props.defaultSelectedRow : -1; this.props.updateWidgetMetaProperty("selectedRowIndex", selectedRowIndex); } else { const selectedRowIndices = Array.isArray(this.props.defaultSelectedRow) ? this.props.defaultSelectedRow : []; this.props.updateWidgetMetaProperty( "selectedRowIndices", selectedRowIndices, ); } }; handlePrevPageClick = () => { let pageNo = this.props.pageNo || 1; pageNo = pageNo - 1; if (pageNo >= 1) { this.props.updateWidgetMetaProperty("pageNo", pageNo, { dynamicString: this.props.onPageChange, event: { type: EventType.ON_PREV_PAGE, }, }); if (this.props.onPageChange) { this.resetSelectedRowIndex(); } } }; getWidgetType(): WidgetType { return "TABLE_WIDGET"; } } export default TableWidget; export const ProfiledTableWidget = Sentry.withProfiler(withMeta(TableWidget));