diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 592839224..5c3f1c41c 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -721,6 +721,17 @@ const DataGrid = () => { + + + <DxcDataGrid + columns={columns} + rows={[]} + uniqueRowId="id" + selectable + selectedRows={selectedChildRows} + onSelectRows={setSelectedChildRows} + /> + </ExampleContainer> <ExampleContainer> <Title title="Controlled Rows" theme="light" level={4} /> <DxcDataGrid @@ -922,6 +933,143 @@ const DataGridSortedExpandable = () => { ); }; +const DataGridUnknownUniqueRowId = () => { + const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); + const [selectedChildRows, setSelectedChildRows] = useState((): Set<number | string> => new Set()); + + const [itemsPerPage, setItemsPerPage] = useState(5); + const [rowsControlled, setRowsControlled] = useState(expandableRows.slice(0, itemsPerPage)); + const [page, setPage] = useState(0); + + return ( + <> + <ExampleContainer> + <Title title="Default" theme="light" level={4} /> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="error" /> + </ExampleContainer> + <ExampleContainer> + <Title title="Expandable" theme="light" level={4} /> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="error" expandable /> + </ExampleContainer> + <ExampleContainer> + <Title title="Selectable" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="error" + selectable + selectedRows={selectedRows} + onSelectRows={setSelectedRows} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Selectable & expandable" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + uniqueRowId="error" + expandable + selectable + selectedRows={selectedRows} + onSelectRows={setSelectedRows} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="DataGrid with children" theme="light" level={4} /> + <DxcDataGrid columns={childcolumns} rows={childRows} uniqueRowId="error" /> + </ExampleContainer> + <ExampleContainer> + <Title title="DataGrid with children" theme="light" level={4} /> + <DxcDataGrid + columns={childcolumns} + rows={childRows} + uniqueRowId="error" + selectable + selectedRows={selectedChildRows} + onSelectRows={setSelectedChildRows} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Summary row" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={expandableRows} + summaryRow={{ label: "Total", total: 100 }} + uniqueRowId="error" + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Scrollable Data Grid" theme="light" level={4} /> + <DxcContainer height="250px"> + <DxcDataGrid columns={columns} rows={expandableRows} uniqueRowId="error" /> + </DxcContainer> + </ExampleContainer> + <ExampleContainer> + <Title title="Empty Data Grid" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={[]} + uniqueRowId="error" + selectable + selectedRows={selectedChildRows} + onSelectRows={setSelectedChildRows} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Controlled Rows" theme="light" level={4} /> + <DxcDataGrid + columns={columns} + rows={rowsControlled} + uniqueRowId="error" + showPaginator + onSort={(sortColumn) => { + if (sortColumn) { + const { columnKey, direction } = sortColumn; + console.log(`Sorting the column '${columnKey}' by '${direction}' direction`); + setRowsControlled((currentRows) => { + return currentRows.sort((a, b) => { + if (isKeyOfRow(columnKey, a) && isKeyOfRow(columnKey, b)) { + const valueA = a[columnKey]; + const valueB = b[columnKey]; + if (valueA != null && valueB != null) { + if (direction === "ASC") { + return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + } else { + return valueA < valueB ? 1 : valueA > valueB ? -1 : 0; + } + } else { + return 0; + } + } else { + return 0; + } + }); + }); + } else { + console.log("Removed sorting criteria"); + setRowsControlled(expandableRows.slice(page * itemsPerPage, page * itemsPerPage + itemsPerPage)); + } + }} + onPageChange={(page) => { + const internalPage = page - 1; + setPage(internalPage); + setRowsControlled( + expandableRows.slice(internalPage * itemsPerPage, internalPage * itemsPerPage + itemsPerPage) + ); + }} + itemsPerPage={itemsPerPage} + itemsPerPageOptions={[5, 10]} + itemsPerPageFunction={(n) => { + setItemsPerPage(n); + setRowsControlled(expandableRows.slice(0, n)); + }} + totalItems={expandableRows.length} + /> + </ExampleContainer> + </> + ); +}; + type Story = StoryObj<typeof DxcDataGrid>; export const Chromatic: Story = { @@ -991,3 +1139,7 @@ export const DataGridSortedExpanded: Story = { button37 && (await userEvent.click(button37)); }, }; + +export const UnknownUniqueId: Story = { + render: DataGridUnknownUniqueRowId, +}; \ No newline at end of file diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 9b483666f..9fe2cc1e2 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -197,26 +197,30 @@ const DxcDataGrid = ({ const sortedRows = useMemo((): readonly GridRow[] | HierarchyGridRow[] | ExpandableGridRow[] => { const sortFunctions = getCustomSortFn(columns); if (!onSort) { - if (expandable && sortColumns.length > 0) { - const innerSortedRows = sortRows( - rowsToRender.filter((row) => !row.isExpandedChildContent), - sortColumns, - sortFunctions - ); - rowsToRender - .filter((row) => row.isExpandedChildContent) - .map((expandedRow) => - addRow( - innerSortedRows, - innerSortedRows.findIndex((trigger) => rowKeyGetter(trigger, uniqueRowId) === expandedRow.triggerRowKey) + - 1, - expandedRow - ) + if (sortColumns.length > 0 && uniqueRowId) { + if (expandable) { + const innerSortedRows = sortRows( + rowsToRender.filter((row) => !row.isExpandedChildContent), + sortColumns, + sortFunctions ); - return innerSortedRows; - } - if (!expandable && sortColumns.length > 0 && uniqueRowId) { - return sortHierarchyRows(rowsToRender, sortColumns, sortFunctions, uniqueRowId); + if (innerSortedRows.some((row) => uniqueRowId in row)) { + rowsToRender + .filter((row) => row.isExpandedChildContent) + .map((expandedRow) => + addRow( + innerSortedRows, + innerSortedRows.findIndex( + (trigger) => rowKeyGetter(trigger, uniqueRowId) === expandedRow.triggerRowKey + ) + 1, + expandedRow + ) + ); + return innerSortedRows; + } + } else { + return sortHierarchyRows(rowsToRender, sortColumns, sortFunctions, uniqueRowId); + } } } return rowsToRender; diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 7c08c2df8..f10bde38d 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -122,6 +122,7 @@ export const renderExpandableTrigger = ( }); } }} + disabled={!rows.some((row) => uniqueRowId in row)} /> ); @@ -143,6 +144,7 @@ export const renderHierarchyTrigger = ( ) => ( <button type="button" + disabled={!rows.some((row) => uniqueRowId in row)} onClick={() => { let newRowsToRender = [...rows]; if (!triggerRow.visibleChildren) { @@ -223,6 +225,7 @@ export const renderCheckbox = ( } onSelectRows(selected); }} + disabled={!rows.some((row) => uniqueRowId in row)} /> ); @@ -244,7 +247,7 @@ export const renderHeaderCheckbox = ( ) => ( <HalstackProvider advancedTheme={overwriteTheme(colorsTheme)}> <DxcCheckbox - checked={!rows.some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} + checked={rows.length > 0 && !rows.some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} onChange={(checked) => { const updatedSelection = new Set(selectedRows); @@ -266,6 +269,7 @@ export const renderHeaderCheckbox = ( onSelectRows(updatedSelection); }} + disabled={rows.length === 0 || !rows.some((row) => uniqueRowId in row)} /> </HalstackProvider> ); @@ -384,7 +388,7 @@ export const sortHierarchyRows = ( ); // add children directly under the parent if it is available while (sortedChildren.length) { - if (uniqueRowId) { + if (uniqueRowId && sortedChildren.some((row) => uniqueRowId in row)) { sortedChildren = sortedChildren.reduce( ( remainingChilds: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], @@ -498,6 +502,9 @@ export const getParentSelectedState = ( selectedRows: Set<ReactNode>, checkedStateToMatch: boolean ) => { + if (!rowList.some((row) => uniqueRowId in row)) { + return; + } const parentRow = rowFinderBasedOnId(rowList, uniqueRowId, parentKeyValue) as HierarchyGridRow; if (!parentRow) { @@ -601,7 +608,7 @@ export const getPaginatedNodes = ( * @param {string} key - The key to check if it exists in the row object. * @param {T} obj - The row object to check the key against. * @returns {boolean} - Returns `true` if `key` is a valid key of `obj`, otherwise `false`. - * + * */ export const isKeyOfRow = <T extends GridRow>(key: string, obj: T): key is Extract<keyof T, string> => { return key in obj;