From 3886ea7da7be8ccd02824daa6cbaa55d566afa76 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Fri, 30 May 2025 14:37:46 +0200 Subject: [PATCH 1/6] Modified state handling for rows to prevent reinitialization --- .../lib/src/data-grid/DataGrid.stories.tsx | 3 + packages/lib/src/data-grid/DataGrid.tsx | 29 +++----- packages/lib/src/data-grid/utils.tsx | 74 +++++++++++++------ 3 files changed, 65 insertions(+), 41 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index ea4a0728f..f3e063fec 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -788,6 +788,9 @@ const DataGridControlled = () => { setRowsControlled(expandableRows.slice(0, n)); }} totalItems={expandableRows.length} + // onGridRowsChange={(row) => { + // console.log(`Modified row: ${row}`); + // }} /> diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index a21c4ffa7..2f30e371b 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -18,6 +18,7 @@ import { getPaginatedNodes, getMinItemsPerPageIndex, getMaxItemsPerPageIndex, + expandRow, } from "./utils"; import DxcPaginator from "../paginator/Paginator"; import { DxcActionsCell } from "../table/Table"; @@ -171,7 +172,7 @@ const DxcDataGrid = ({ totalItems, defaultPage = 1, }: DataGridPropsType): JSX.Element => { - const [rowsToRender, setRowsToRender] = useState(rows); + const [rowsToRender, setRowsToRender] = useState([...rows]); const [page, changePage] = useState(defaultPage); const [colHeight, setColHeight] = useState(36); @@ -290,25 +291,17 @@ const DxcDataGrid = ({ }, [columnsToRender.length]); useEffect(() => { - const finalRows = [...rows]; if (expandable) { - rows.forEach((row, index) => { - if ( - row.contentIsExpanded && - !rows.some((row) => row[uniqueRowId] === `${rowKeyGetter(row, uniqueRowId)}_expanded`) - ) { - addRow(finalRows, index + 1, { - isExpandedChildContent: row.contentIsExpanded, - [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, - expandedChildContent: row.expandedContent, - triggerRowKey: rowKeyGetter(row, uniqueRowId), - expandedContentHeight: row.expandedContentHeight, - }); - } - }); + rows + .filter((row) => { + const rowId = rowKeyGetter(row, uniqueRowId); + return row.contentIsExpanded && !rows.some((r) => r[uniqueRowId] === `${rowId}_expanded`); + }) + .forEach((row) => { + expandRow(row, rows, uniqueRowId, setRowsToRender); + }); } - setRowsToRender(finalRows); - }, [rows]); + }, []); const reorderedColumns = useMemo( () => diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 0aa8f13a2..8e72d45ee 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -59,6 +59,52 @@ export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => ( ); +/** + * Expands a given row by inserting a new child row with the expanded content. + * @param {ExpandableGridRow} row - The row object to expand. + * @param {ExpandableGridRow[]} rows - The current list of all rows (as rendered). + * @param {string} uniqueRowId - Unique identifier key used for each row. + * @param {Function} setRowsToRender - State setter to update the rendered rows. + */ +export const expandRow = ( + row: ExpandableGridRow, + rows: ExpandableGridRow[], + uniqueRowId: string, + setRowsToRender: (_value: SetStateAction) => void +) => { + const rowIndex = rows.findIndex((r) => r === row); + setRowsToRender((currentRows) => { + const newRows = [...currentRows]; + addRow(newRows, rowIndex + 1, { + isExpandedChildContent: true, + [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, + expandedChildContent: row.expandedContent, + triggerRowKey: rowKeyGetter(row, uniqueRowId), + expandedContentHeight: row.expandedContentHeight, + }); + return newRows; + }); +}; + +/** + * Collapses a given row by removing its expanded child row. + * @param {ExpandableGridRow} row - The row object to collapse. + * @param {ExpandableGridRow[]} rows - The current list of all rows (as rendered). + * @param {Function} setRowsToRender - State setter to update the rendered rows. + */ +export const collapseRow = ( + row: ExpandableGridRow, + rows: ExpandableGridRow[], + setRowsToRender: (_value: SetStateAction) => void +) => { + const rowIndex = rows.findIndex((r) => r === row); + setRowsToRender((currentRows) => { + const newRows = [...currentRows]; + deleteRow(newRows, rowIndex + 1); + return newRows; + }); +}; + /** * Renders an expandable trigger icon that toggles row expansion. * @param {ExpandableGridRow} row - Row object that can be expanded or collapsed. @@ -80,25 +126,9 @@ export const renderExpandableTrigger = ( onClick={() => { row.contentIsExpanded = !row.contentIsExpanded; if (row.contentIsExpanded) { - const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((currentRows) => { - const newRows = [...currentRows]; - addRow(newRows, rowIndex + 1, { - isExpandedChildContent: row.contentIsExpanded, - [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, - expandedChildContent: row.expandedContent, - triggerRowKey: rowKeyGetter(row, uniqueRowId), - expandedContentHeight: row.expandedContentHeight, - }); - return newRows; - }); + expandRow(row, rows, uniqueRowId, setRowsToRender); } else { - const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((currentRows) => { - const newRows = [...currentRows]; - deleteRow(newRows, rowIndex + 1); - return newRows; - }); + collapseRow(row, rows, setRowsToRender); } }} disabled={!rows.some((row) => uniqueRowId in row)} @@ -136,11 +166,9 @@ export const renderHierarchyTrigger = ( }); } else { // The children of the row that is being collapsed are added to an array - const rowsToRemove: HierarchyGridRow[] = [ - ...rows.filter( - (rowToRender) => rowToRender.parentKey && rowToRender.parentKey === rowKeyGetter(triggerRow, uniqueRowId) - ), - ]; + const rowsToRemove: HierarchyGridRow[] = rows.filter( + (rowToRender) => rowToRender.parentKey && rowToRender.parentKey === rowKeyGetter(triggerRow, uniqueRowId) + ); // The children are checked if any of them has any other children of their own const rowsToCheck = [...rowsToRemove]; while (rowsToCheck.length > 0) { From 1a2cd25d2d06e20b8d1ec932511d3661805eb168 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Mon, 2 Jun 2025 16:09:04 +0200 Subject: [PATCH 2/6] Fixed example for selectable hierarchical datagrid and refactored code --- .../code/examples/hierarchicalSelectable.ts | 13 +++--- packages/lib/src/data-grid/DataGrid.tsx | 8 ++-- packages/lib/src/data-grid/utils.tsx | 45 +++++++------------ 3 files changed, 27 insertions(+), 39 deletions(-) diff --git a/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts index 47de08593..eb457a2ed 100644 --- a/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts +++ b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts @@ -1,8 +1,8 @@ import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; -import { useState } from "react"; +import { useMemo, useState } from "react"; const code = `() => { - const columns = [ + const columns = useMemo(() => [ { key: "name", label: "Label", @@ -14,9 +14,9 @@ const code = `() => { alignment: "right", summaryKey: "total" }, - ]; - - const rows = [ + ], []); + + const rows = useMemo(() => [ { name: "Root Node 1", value: "1", @@ -75,7 +75,7 @@ const code = `() => { }, ], }, - ]; + ], []); const [selectedRows, setSelectedRows] = useState(new Set()); return ( @@ -96,6 +96,7 @@ const scope = { DxcDataGrid, DxcInset, useState, + useMemo, }; export default { code, scope }; diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 2f30e371b..eac2f5ef2 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -291,17 +291,19 @@ const DxcDataGrid = ({ }, [columnsToRender.length]); useEffect(() => { + const finalRows = [...rows]; if (expandable) { - rows + finalRows .filter((row) => { const rowId = rowKeyGetter(row, uniqueRowId); return row.contentIsExpanded && !rows.some((r) => r[uniqueRowId] === `${rowId}_expanded`); }) .forEach((row) => { - expandRow(row, rows, uniqueRowId, setRowsToRender); + expandRow(row, finalRows, uniqueRowId); }); } - }, []); + setRowsToRender(finalRows); + }, [rows]); const reorderedColumns = useMemo( () => diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 8e72d45ee..6606b0c77 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -64,45 +64,30 @@ export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => ( * @param {ExpandableGridRow} row - The row object to expand. * @param {ExpandableGridRow[]} rows - The current list of all rows (as rendered). * @param {string} uniqueRowId - Unique identifier key used for each row. - * @param {Function} setRowsToRender - State setter to update the rendered rows. */ -export const expandRow = ( - row: ExpandableGridRow, - rows: ExpandableGridRow[], - uniqueRowId: string, - setRowsToRender: (_value: SetStateAction) => void -) => { +export const expandRow = (row: ExpandableGridRow, rows: ExpandableGridRow[], uniqueRowId: string) => { const rowIndex = rows.findIndex((r) => r === row); - setRowsToRender((currentRows) => { - const newRows = [...currentRows]; - addRow(newRows, rowIndex + 1, { - isExpandedChildContent: true, - [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, - expandedChildContent: row.expandedContent, - triggerRowKey: rowKeyGetter(row, uniqueRowId), - expandedContentHeight: row.expandedContentHeight, - }); - return newRows; + const newRows = [...rows]; + addRow(newRows, rowIndex + 1, { + isExpandedChildContent: true, + [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, + expandedChildContent: row.expandedContent, + triggerRowKey: rowKeyGetter(row, uniqueRowId), + expandedContentHeight: row.expandedContentHeight, }); + return newRows; }; /** * Collapses a given row by removing its expanded child row. * @param {ExpandableGridRow} row - The row object to collapse. * @param {ExpandableGridRow[]} rows - The current list of all rows (as rendered). - * @param {Function} setRowsToRender - State setter to update the rendered rows. */ -export const collapseRow = ( - row: ExpandableGridRow, - rows: ExpandableGridRow[], - setRowsToRender: (_value: SetStateAction) => void -) => { +export const collapseRow = (row: ExpandableGridRow, rows: ExpandableGridRow[]) => { const rowIndex = rows.findIndex((r) => r === row); - setRowsToRender((currentRows) => { - const newRows = [...currentRows]; - deleteRow(newRows, rowIndex + 1); - return newRows; - }); + const newRows = [...rows]; + deleteRow(newRows, rowIndex + 1); + return newRows; }; /** @@ -126,9 +111,9 @@ export const renderExpandableTrigger = ( onClick={() => { row.contentIsExpanded = !row.contentIsExpanded; if (row.contentIsExpanded) { - expandRow(row, rows, uniqueRowId, setRowsToRender); + setRowsToRender((currentRows) => expandRow(row, [...currentRows], uniqueRowId)); } else { - collapseRow(row, rows, setRowsToRender); + setRowsToRender((currentRows) => collapseRow(row, [...currentRows])); } }} disabled={!rows.some((row) => uniqueRowId in row)} From c40d107d6e9a71c6a2737c4ef03715fd8c22b8cc Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Thu, 12 Jun 2025 15:59:04 +0200 Subject: [PATCH 3/6] Removed unneeded code --- packages/lib/src/data-grid/DataGrid.stories.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index f3e063fec..ea4a0728f 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -788,9 +788,6 @@ const DataGridControlled = () => { setRowsControlled(expandableRows.slice(0, n)); }} totalItems={expandableRows.length} - // onGridRowsChange={(row) => { - // console.log(`Modified row: ${row}`); - // }} /> From 6a15b40adc4af4dd57614109c29d1c578456b0df Mon Sep 17 00:00:00 2001 From: Jialecl Date: Fri, 27 Jun 2025 11:21:58 +0200 Subject: [PATCH 4/6] Changing to useState instead of useMemo --- .../data-grid/code/examples/hierarchicalSelectable.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts index eb457a2ed..8847aa7dd 100644 --- a/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts +++ b/apps/website/screens/components/data-grid/code/examples/hierarchicalSelectable.ts @@ -1,8 +1,8 @@ import { DxcDataGrid, DxcInset } from "@dxc-technology/halstack-react"; -import { useMemo, useState } from "react"; +import { useState } from "react"; const code = `() => { - const columns = useMemo(() => [ + const [columns] = useState([ { key: "name", label: "Label", @@ -14,9 +14,9 @@ const code = `() => { alignment: "right", summaryKey: "total" }, - ], []); + ]); - const rows = useMemo(() => [ + const [rows] = useState([ { name: "Root Node 1", value: "1", @@ -75,7 +75,7 @@ const code = `() => { }, ], }, - ], []); + ]); const [selectedRows, setSelectedRows] = useState(new Set()); return ( @@ -96,7 +96,6 @@ const scope = { DxcDataGrid, DxcInset, useState, - useMemo, }; export default { code, scope }; From 12a87815de8a8783cab050f119f4f8e8cddcd7b1 Mon Sep 17 00:00:00 2001 From: Jialecl Date: Fri, 27 Jun 2025 13:02:48 +0200 Subject: [PATCH 5/6] expandRow function fixed --- packages/lib/src/data-grid/utils.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 6606b0c77..fdbc18472 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -67,15 +67,13 @@ export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => ( */ export const expandRow = (row: ExpandableGridRow, rows: ExpandableGridRow[], uniqueRowId: string) => { const rowIndex = rows.findIndex((r) => r === row); - const newRows = [...rows]; - addRow(newRows, rowIndex + 1, { + addRow(rows, rowIndex + 1, { isExpandedChildContent: true, [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, expandedChildContent: row.expandedContent, triggerRowKey: rowKeyGetter(row, uniqueRowId), expandedContentHeight: row.expandedContentHeight, }); - return newRows; }; /** From 08fbe90e0023de810b49c541a55cf3d425e815c9 Mon Sep 17 00:00:00 2001 From: Jialecl Date: Fri, 27 Jun 2025 14:02:25 +0200 Subject: [PATCH 6/6] type bug fixed --- packages/lib/src/data-grid/utils.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index fdbc18472..22ea80024 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -109,7 +109,11 @@ export const renderExpandableTrigger = ( onClick={() => { row.contentIsExpanded = !row.contentIsExpanded; if (row.contentIsExpanded) { - setRowsToRender((currentRows) => expandRow(row, [...currentRows], uniqueRowId)); + setRowsToRender((currentRows) => { + const finalRows = [...currentRows]; + expandRow(row, finalRows, uniqueRowId); + return finalRows; + }); } else { setRowsToRender((currentRows) => collapseRow(row, [...currentRows])); }