diff --git a/packages/react-table/src/components/Table/Table.tsx b/packages/react-table/src/components/Table/Table.tsx index 8dbaaf0a8d3..afd2b26c5f4 100644 --- a/packages/react-table/src/components/Table/Table.tsx +++ b/packages/react-table/src/components/Table/Table.tsx @@ -57,6 +57,8 @@ export interface TableProps extends React.HTMLProps, OUIAProps isStriped?: boolean; /** Flag indicating this table contains expandable rows to maintain proper striping */ isExpandable?: boolean; + /** Flag indicating this table's rows will not have the inset typically reserved for expanding/collapsing rows in a tree table. Intended for use on tree tables with no visible rows with children. */ + hasNoInset?: boolean; /** Collection of column spans for nested headers. Deprecated: see https://github.com/patternfly/patternfly/issues/4584 */ nestedHeaderColumnSpans?: number[]; /** Visible text to add alongside the hidden a11y caption for tables with selectable rows. */ @@ -91,6 +93,7 @@ const TableBase: React.FunctionComponent = ({ isNested = false, isStriped = false, isExpandable = false, + hasNoInset = false, // eslint-disable-next-line @typescript-eslint/no-unused-vars nestedHeaderColumnSpans, selectableRowCaptionText, @@ -206,6 +209,7 @@ const TableBase: React.FunctionComponent = ({ isTreeTable && stylesTreeView.modifiers.treeView, isStriped && styles.modifiers.striped, isExpandable && styles.modifiers.expandable, + hasNoInset && stylesTreeView.modifiers.noInset, isNested && 'pf-m-nested' )} ref={tableRef} diff --git a/packages/react-table/src/components/Table/examples/Table.md b/packages/react-table/src/components/Table/examples/Table.md index 4cb3c7875d5..3a8c8010614 100644 --- a/packages/react-table/src/components/Table/examples/Table.md +++ b/packages/react-table/src/components/Table/examples/Table.md @@ -297,6 +297,13 @@ the voice over technologies will recognize the flat table structure as a tree. ```ts file="TableTree.tsx" ``` +### Flat tree table with no inset + +To remove the inset used to leave space for the expand/collapse toggle in a flat tree table, use the `hasNoInset` prop on the `Table` component. + +```ts file="TableTreeNoInset.tsx" +``` + ### Draggable row table To make a row draggable: diff --git a/packages/react-table/src/components/Table/examples/TableTreeNoInset.tsx b/packages/react-table/src/components/Table/examples/TableTreeNoInset.tsx new file mode 100644 index 00000000000..3c16708b9a6 --- /dev/null +++ b/packages/react-table/src/components/Table/examples/TableTreeNoInset.tsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { Table, Thead, Tr, Th, Tbody, Td, TreeRowWrapper, TdProps } from '@patternfly/react-table'; +import LeafIcon from '@patternfly/react-icons/dist/esm/icons/leaf-icon'; + +interface RepositoriesTreeNode { + name: string; + branches: string; + pullRequests: string; + workspaces: string; + children?: RepositoriesTreeNode[]; +} + +export const TableTreeNoInset: React.FunctionComponent = () => { + // In real usage, this data would come from some external source like an API via props. + const data: RepositoriesTreeNode[] = [ + { + name: 'Repositories one', + branches: 'Branch one', + pullRequests: 'Pull request one', + workspaces: 'Workplace one', + children: [] + }, + { + name: 'Repositories seven', + branches: 'Branch seven', + pullRequests: 'Pull request seven', + workspaces: 'Workplace seven', + children: [] + }, + { + name: 'Repositories nine', + branches: 'Branch nine', + pullRequests: 'Pull request nine', + workspaces: 'Workplace nine' + } + ]; + + const columnNames = { + name: 'Repositories', + branches: 'Branches', + prs: 'Pull requests', + workspaces: 'Workspaces' + }; + + const [selectedNodeNames, setSelectedNodeNames] = React.useState([]); + const isNodeChecked = (node: RepositoriesTreeNode) => selectedNodeNames.includes(node.name); + + /** + Recursive function which flattens the data into an array of flattened TreeRowWrapper components + params: + - nodes - array of a single level of tree nodes + - level - number representing how deeply nested the current row is + - posinset - position of the row relative to this row's siblings + - currentRowIndex - position of the row relative to the entire table + - isHidden - defaults to false, true if this row's parent is expanded + */ + const renderRows = ( + [node, ...remainingNodes]: RepositoriesTreeNode[], + level = 1, + posinset = 1, + rowIndex = 0, + isHidden = false + ): React.ReactNode[] => { + if (!node) { + return []; + } + const isChecked = isNodeChecked(node); + const icon = ; + + const treeRow: TdProps['treeRow'] = { + onCollapse: () => {}, + onToggleRowDetails: () => {}, + onCheckChange: (_event, isChecking) => { + setSelectedNodeNames(prevSelected => { + const otherSelectedNodeNames = prevSelected.filter(name => name === node.name); + return !isChecking ? otherSelectedNodeNames : [...otherSelectedNodeNames, node.name]; + }); + }, + rowIndex, + props: { + isHidden, + 'aria-level': level, + 'aria-posinset': posinset, + 'aria-setsize': node.children ? node.children.length : 0, + isChecked, + checkboxId: `flat_checkbox_id_${node.name.toLowerCase().replace(/\s+/g, '_')}`, + icon + } + }; + + const childRows = + node.children && node.children.length + ? renderRows(node.children, level + 1, 1, rowIndex + 1, isHidden) + : []; + + return [ + + + {node.name} + + {node.branches} + {node.pullRequests} + {node.workspaces} + , + ...childRows, + ...renderRows(remainingNodes, level, posinset + 1, rowIndex + 1 + childRows.length, isHidden) + ]; + }; + + return ( + + + + + + + + + + {renderRows(data)} +
{columnNames.name}{columnNames.branches}{columnNames.prs}{columnNames.workspaces}
+ ); +};