= ({
{(rowIsObject ? row.row : row).map((cell, colIndex) => {
const cellIsObject = isDataViewTdObject(cell);
const cellExpandableContent = isExpandable ? expandedRows?.find(
- (content) => content.rowId === rowId && content.columnId === colIndex
+ (content) => (indexBy ? String(content.rowId) === String(rowKey) : content.rowId === rowId) && content.columnId === colIndex
) : undefined;
return (
= ({
{...(cellExpandableContent != null && {
compoundExpand: {
isExpanded: isRowExpanded && expandedColIndex === colIndex,
- expandId: `expandable-${rowIndex}`,
+ expandId: `expandable-${rowKey}`,
onToggle: () => {
setExpandedRowsState(prev => {
const isSameColumn = expandedColIndex === colIndex;
- const wasExpanded = prev[rowIndex];
- return { ...prev, [rowIndex]: isSameColumn ? !wasExpanded : true };
+ const wasExpanded = prev[rowKey];
+ return { ...prev, [rowKey]: isSameColumn ? !wasExpanded : true };
});
- setExpandedColumnIndex(prev => ({ ...prev, [rowIndex]: colIndex }));
+ setExpandedColumnIndex(prev => ({ ...prev, [rowKey]: colIndex }));
},
rowIndex,
columnIndex: colIndex
@@ -149,7 +158,7 @@ export const DataViewTableBasic: FC = ({
} else {
return rowContent;
}
- }), [ rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId, expandedRowsState, expandedColumnIndex, expandedRows, isExpandable, needsSeparateTbody ]);
+ }), [ rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId, expandedRowsState, expandedColumnIndex, expandedRows, isExpandable, needsSeparateTbody, indexBy ]);
const bodyContent = activeBodyState || (needsSeparateTbody ? renderedRows : {renderedRows});
diff --git a/packages/module/src/DataViewTableTree/DataViewTableTree.test.tsx b/packages/module/src/DataViewTableTree/DataViewTableTree.test.tsx
index f76d40b3..62cb3579 100644
--- a/packages/module/src/DataViewTableTree/DataViewTableTree.test.tsx
+++ b/packages/module/src/DataViewTableTree/DataViewTableTree.test.tsx
@@ -114,8 +114,28 @@ describe('DataViewTableTree component', () => {
const { container } = render(
-
+
+ );
+ expect(container).toMatchSnapshot();
+ });
+
+ test('should render tree table with indexBy prop', () => {
+ const { container } = render(
+
+ } expandedIcon={} collapsedIcon={} />
+
+ );
+ expect(container.querySelectorAll('tr').length).toBeGreaterThan(0);
+ });
+
+ test('should render tree table with expandAll and indexBy', () => {
+ const { container } = render(
+
+ } expandedIcon={} collapsedIcon={} />
+
);
+ // With expandAll, all expandable rows should be rendered as expanded
+ expect(container.querySelectorAll('tr').length).toBeGreaterThan(0);
expect(container).toMatchSnapshot();
});
});
diff --git a/packages/module/src/DataViewTableTree/DataViewTableTree.tsx b/packages/module/src/DataViewTableTree/DataViewTableTree.tsx
index a76ea52d..7a437b58 100644
--- a/packages/module/src/DataViewTableTree/DataViewTableTree.tsx
+++ b/packages/module/src/DataViewTableTree/DataViewTableTree.tsx
@@ -71,6 +71,8 @@ export interface DataViewTableTreeProps extends Omit = ({
@@ -83,6 +85,7 @@ export const DataViewTableTree: FC = ({
collapsedIcon = null,
expandAll = false,
ouiaId = 'DataViewTableTree',
+ indexBy,
...props
}: DataViewTableTreeProps) => {
const { selection, activeState } = useInternalContext();
@@ -90,26 +93,30 @@ export const DataViewTableTree: FC = ({
const [ expandedNodeIds, setExpandedNodeIds ] = useState([]);
const [ expandedDetailsNodeNames, setExpandedDetailsNodeIds ] = useState([]);
+ /** Resolves the unique identifier for a tree node based on the `indexBy` prop or the default `id` field. */
+ const getNodeId = (node: DataViewTrTree): string =>
+ indexBy && indexBy in node ? String((node as unknown as Record)[indexBy]) : node.id;
+
// Helper function to collect all node IDs that have children (are expandable)
const getExpandableNodeIds = (nodes: DataViewTrTree[]): string[] => {
const expandableIds: string[] = [];
-
+
const traverse = (nodeList: DataViewTrTree[]) => {
nodeList.forEach(node => {
if (node.children && node.children.length > 0) {
- expandableIds.push(node.id);
+ expandableIds.push(getNodeId(node));
traverse(node.children);
}
});
};
-
+
traverse(nodes);
return expandableIds;
};
// Effect to handle expandAll behavior
// Memoize the expandable IDs to avoid recalculating when rows object reference changes but structure is the same
- const expandableIds = useMemo(() => getExpandableNodeIds(rows), [ rows ]);
+ const expandableIds = useMemo(() => getExpandableNodeIds(rows), [ rows, indexBy ]); // eslint-disable-line react-hooks/exhaustive-deps
// Effect to handle expandAll behavior - only runs when IDs actually change
useEffect(() => {
@@ -136,8 +143,9 @@ export const DataViewTableTree: FC = ({
if (!node) {
return [];
}
- const isExpanded = expandedNodeIds.includes(node.id);
- const isDetailsExpanded = expandedDetailsNodeNames.includes(node.id);
+ const nodeId = getNodeId(node);
+ const isExpanded = expandedNodeIds.includes(nodeId);
+ const isDetailsExpanded = expandedDetailsNodeNames.includes(nodeId);
const isChecked = isSelected?.(node);
let icon = leafIcon;
if (node.children) {
@@ -147,13 +155,13 @@ export const DataViewTableTree: FC = ({
const treeRow: TdProps['treeRow'] = {
onCollapse: () =>
setExpandedNodeIds(prevExpanded => {
- const otherExpandedNodeIds = prevExpanded.filter(id => id !== node.id);
- return isExpanded ? otherExpandedNodeIds : [ ...otherExpandedNodeIds, node.id ];
+ const otherExpandedNodeIds = prevExpanded.filter(id => id !== nodeId);
+ return isExpanded ? otherExpandedNodeIds : [ ...otherExpandedNodeIds, nodeId ];
}),
onToggleRowDetails: () =>
setExpandedDetailsNodeIds(prevDetailsExpanded => {
- const otherDetailsExpandedNodeIds = prevDetailsExpanded.filter(id => id !== node.id);
- return isDetailsExpanded ? otherDetailsExpandedNodeIds : [ ...otherDetailsExpandedNodeIds, node.id ];
+ const otherDetailsExpandedNodeIds = prevDetailsExpanded.filter(id => id !== nodeId);
+ return isDetailsExpanded ? otherDetailsExpandedNodeIds : [ ...otherDetailsExpandedNodeIds, nodeId ];
}),
onCheckChange: (isSelectDisabled?.(node) || !onSelect) ? undefined : (_event, isChecking) => onSelect?.(isChecking, getNodesAffectedBySelection(rows, node, isChecking, isSelected)),
rowIndex,
@@ -165,7 +173,7 @@ export const DataViewTableTree: FC = ({
'aria-posinset': posinset,
'aria-setsize': node.children?.length ?? 0,
isChecked,
- checkboxId: `checkbox_id_${node.id?.toLowerCase().replace(/\s+/g, '_')}`,
+ checkboxId: `checkbox_id_${nodeId?.toLowerCase().replace(/\s+/g, '_')}`,
icon,
},
};
@@ -175,7 +183,7 @@ export const DataViewTableTree: FC = ({
: [];
return [
-
+
{node.row.map((cell, colIndex) => {
const cellIsObject = isDataViewTdObject(cell);
return (
@@ -206,7 +214,8 @@ export const DataViewTableTree: FC = ({
isSelected,
onSelect,
isSelectDisabled,
- ouiaId
+ ouiaId,
+ indexBy
]);
return (
diff --git a/packages/module/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap b/packages/module/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap
index c541ae09..2e75879e 100644
--- a/packages/module/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap
+++ b/packages/module/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap
@@ -2147,3 +2147,960 @@ exports[`DataViewTableTree component should render tree table with an error stat
`;
+
+exports[`DataViewTableTree component should render tree table with expandAll and indexBy 1`] = `
+
+
+
+
+
+
+ |
+ Repositories
+ |
+
+ Branches
+ |
+
+ Pull requests
+ |
+
+ Workspaces
+ |
+
+ Last commit
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository one
+
+
+
+
+
+
+ |
+
+ Branch one
+ |
+
+ Pull request one
+ |
+
+ Workspace one
+ |
+
+ Timestamp one
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository two
+
+
+
+
+
+
+ |
+
+ Branch two
+ |
+
+ Pull request two
+ |
+
+ Workspace two
+ |
+
+ Timestamp two
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository three
+
+
+
+
+
+
+ |
+
+ Branch three
+ |
+
+ Pull request three
+ |
+
+ Workspace three
+ |
+
+ Timestamp three
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository four
+
+
+
+
+
+
+ |
+
+ Branch four
+ |
+
+ Pull request four
+ |
+
+ Workspace four
+ |
+
+ Timestamp four
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository five
+
+
+
+
+
+
+ |
+
+ Branch five
+ |
+
+ Pull request five
+ |
+
+ Workspace five
+ |
+
+ Timestamp five
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ Repository six
+
+
+
+
+
+
+ |
+
+ Branch six
+ |
+
+ Pull request six
+ |
+
+ Workspace six
+ |
+
+ Timestamp six
+ |
+
+
+
+
+
+
+`;
|