+);
diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
index 23a63a83..35a3114d 100644
--- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
+++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
@@ -58,6 +58,85 @@ If you want to have all expandable nodes open on initial load pass the `expandAl
```
+## Expandable rows
+
+To add expandable content to table cells, pass an array of `ExpandableContent` objects to the `expandedRows` prop of the `` component. Each expandable content object defines which cell can be expanded and what content to display when expanded.
+
+The `ExpandableContent` interface is defined as:
+
+```typescript
+interface ExpandableContent {
+ /** The ID of the row containing the expandable cell (must match the id property in the row data) */
+ row_id: number;
+ /** The column index (0-based) that should be expandable */
+ column_id: number;
+ /** The content to display when the cell is expanded */
+ content: ReactNode;
+}
+```
+
+When a cell has expandable content:
+- A compound expand toggle button appears in the cell
+- Clicking the toggle expands the row to show the additional content below
+- Only one expanded cell is shown per row at a time
+- Clicking another expandable cell in the same row switches the expanded content
+
+### Expandable rows example
+
+```js file="./DataViewTableExpandableExample.tsx"
+
+```
+
+## Sticky header and columns
+
+To enable sticky headers and columns, set the `isSticky` prop to `true` on the `` component. This keeps the table header and designated columns visible when scrolling.
+
+To make specific columns sticky, add the `isStickyColumn` property to the column's `props` in the column definition:
+
+```typescript
+const columns: DataViewTh[] = [
+ { cell: 'Column Name', props: { isStickyColumn: true } }
+];
+```
+
+When sticky headers and columns are enabled:
+- The table header remains visible when scrolling vertically
+- Columns marked with `isStickyColumn: true` remain visible when scrolling horizontally
+- The table is wrapped in `OuterScrollContainer` and `InnerScrollContainer` components to enable sticky behavior
+- Sticky columns can have additional styling like borders using `hasRightBorder` or `hasLeftBorder` props
+
+### Sticky header and columns example
+
+```js file="./DataViewTableStickyExample.tsx"
+
+```
+
+## Draggable rows
+
+To enable drag-and-drop functionality for table rows, set the `isDraggable` prop to `true` on the `` component. This allows users to reorder rows by dragging them to a new position.
+
+When draggable rows are enabled:
+- A drag handle icon appears at the beginning of each row
+- Users can click and drag the handle or anywhere on the row to reorder rows
+- Visual feedback is provided during the drag operation with a ghost row effect
+- The new order is maintained after the drop
+
+The draggable functionality is built using the `useDraggableRows` hook internally, which manages the drag-and-drop state and DOM manipulation.
+
+### Draggable rows example
+
+```js file="./DataViewTableDraggableExample.tsx"
+
+```
+
+### Interactive example
+- Interactive example show how the different composable options work together.
+- By toggling the toggles you can switch between them and observe the bahaviour
+
+```js file="./DataViewTableInteractiveExample.tsx"
+
+```
+
### Resizable columns
To allow a column to resize, add `isResizable` to the `DataViewTable` element, and pass `resizableProps` to each applicable header cell. The `resizableProps` object consists of the following fields:
diff --git a/packages/module/patternfly-docs/generated/index.js b/packages/module/patternfly-docs/generated/index.js
index be42c242..5f6463e6 100644
--- a/packages/module/patternfly-docs/generated/index.js
+++ b/packages/module/patternfly-docs/generated/index.js
@@ -14,8 +14,8 @@ module.exports = {
'/extensions/data-view/table/react': {
id: "Table",
title: "Data view table",
- toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"},{"text":"Resizable columns"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
- examples: ["Table example","Resizable columns","Tree table example","Sorting example","Empty","Error","Loading"],
+ toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"}],{"text":"Expandable rows"},[{"text":"Expandable rows example"}],{"text":"Sticky header and columns"},[{"text":"Sticky header and columns example"}],{"text":"Draggable rows"},[{"text":"Draggable rows example"},{"text":"Interactive example"},{"text":"Resizable columns"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
+ examples: ["Table example","Expandable rows example","Sticky header and columns example","Draggable rows example","Interactive example","Resizable columns","Tree table example","Sorting example","Empty","Error","Loading"],
section: "extensions",
subsection: "Data view",
source: "react",
@@ -26,7 +26,7 @@ module.exports = {
'/extensions/data-view/overview/extensions': {
id: "Overview",
title: "Data view overview",
- toc: [[{"text":"Layout"},{"text":"Modularity"}],{"text":"Events context"},[{"text":"Row click subscription example"}]],
+ toc: [{"text":"How to structure and implement the data view"},[{"text":"Layout"},{"text":"Modularity"}],{"text":"Events context"},[{"text":"Row click subscription example"}]],
examples: ["Layout","Modularity","Row click subscription example"],
section: "extensions",
subsection: "Data view",
diff --git a/packages/module/src/DataViewTable/DataViewTable.tsx b/packages/module/src/DataViewTable/DataViewTable.tsx
index fc96fe17..123c4df5 100644
--- a/packages/module/src/DataViewTable/DataViewTable.tsx
+++ b/packages/module/src/DataViewTable/DataViewTable.tsx
@@ -1,9 +1,11 @@
import { FC, ReactNode } from 'react';
import { TdProps, ThProps, TrProps, InnerScrollContainer } from '@patternfly/react-table';
import { DataViewTableTree, DataViewTableTreeProps } from '../DataViewTableTree';
-import { DataViewTableBasic, DataViewTableBasicProps } from '../DataViewTableBasic';
+import { DataViewTableBasic, DataViewTableBasicProps, ExpandableContent } from '../DataViewTableBasic';
import { DataViewThResizableProps } from '../DataViewTh/DataViewTh';
+export type { ExpandableContent };
+
// Table head typings
export type DataViewTh =
| ReactNode
diff --git a/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx b/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx
index 8208e9e6..4c272f19 100644
--- a/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx
+++ b/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx
@@ -1,15 +1,26 @@
-import { FC, useMemo } from 'react';
+import { FC, useMemo, useState, useRef } from 'react';
import {
+ ExpandableRowContent,
+ InnerScrollContainer,
+ OuterScrollContainer,
Table,
TableProps,
Tbody,
Td,
Tr,
} from '@patternfly/react-table';
+import { GripVerticalIcon } from '@patternfly/react-icons';
import { useInternalContext } from '../InternalContext';
import { DataViewTableHead } from '../DataViewTableHead';
import { DataViewTh, DataViewTr, isDataViewTdObject, isDataViewTrObject } from '../DataViewTable';
import { DataViewState } from '../DataView/DataView';
+import { useDraggableRows } from './hooks';
+
+export interface ExpandableContent {
+ rowId: number;
+ columnId: number;
+ content: React.ReactNode;
+}
/** extends TableProps */
export interface DataViewTableBasicProps extends Omit {
@@ -17,6 +28,8 @@ export interface DataViewTableBasicProps extends Omit>
/** Table body states to be displayed when active */
@@ -25,15 +38,25 @@ export interface DataViewTableBasicProps extends Omit = ({
columns,
rows,
+ expandedRows,
ouiaId = 'DataViewTableBasic',
headStates,
bodyStates,
hasResizableColumns,
+ isExpandable = false,
+ isSticky = false,
+ isDraggable = false,
...props
}: DataViewTableBasicProps) => {
const { selection, activeState, isSelectable } = useInternalContext();
@@ -42,45 +65,127 @@ export const DataViewTableBasic: FC = ({
const activeHeadState = useMemo(() => activeState ? headStates?.[activeState] : undefined, [ activeState, headStates ]);
const activeBodyState = useMemo(() => activeState ? bodyStates?.[activeState] : undefined, [ activeState, bodyStates ]);
+ const [expandedRowsState, setExpandedRowsState] = useState>({})
+ const [expandedColumnIndex, setExpandedColumnIndex] = useState>({})
+
+ const tableRef = useRef(null);
+
+ const {
+ rowIds,
+ onDragStart,
+ onDragEnd,
+ onDrop,
+ onDropTbody,
+ onDragOver,
+ onDragLeave
+ } = useDraggableRows({ rows, tableRef });
+
const renderedRows = useMemo(() => rows.map((row, rowIndex) => {
const rowIsObject = isDataViewTrObject(row);
+ const isRowExpanded = expandedRowsState[rowIndex] || false;
+ const expandedColIndex = expandedColumnIndex[rowIndex];
+
+ // Get the first cell to extract the row ID
+ const rowData = rowIsObject ? row.row : row;
+ const firstCell = rowData[0];
+ const rowId = isDataViewTdObject(firstCell) ? (firstCell as { id?: number }).id : undefined;
+
+ // Find all expandable contents for this row
+ const rowExpandableContents = isExpandable ? expandedRows?.filter(
+ (content) => content.rowId === rowId
+ ) : [];
+
return (
-