Skip to content

Conversation

@Jakub007d
Copy link

@Jakub007d Jakub007d commented Jan 5, 2026

As part of task RHCLOUD-43454

  • Compound options for Expandable, Sticky headers and columns and Draggable rows for DataViewTableBasic
  • Adding new examples for added compound table options to docs

Assisted-by: Claude Code

@patternfly-build
Copy link

patternfly-build commented Jan 5, 2026

@Jakub007d Jakub007d force-pushed the component branch 2 times, most recently from 22f15f0 to cb0a2f4 Compare January 6, 2026 13:20
@Jakub007d Jakub007d changed the title Component [RHCLOUD-43454] Updating data-view docs and adding compound options for Expandable, Sticky and Draggable Jan 6, 2026
@Jakub007d Jakub007d marked this pull request as ready for review January 6, 2026 13:55
Copy link
Collaborator

@radekkaluzik radekkaluzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@nicolethoen nicolethoen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great! I have a number of questions though.

I want to note that the draggable solution here inherits the same lack of keyboard accessibility that PF's existing draggable table solution has. I bet claude could help brainstorm a fix there.. I'll double check PF's table backlog to see if there's a plan for remedying it in our base table component.


export const BasicExample: FunctionComponent = () => (
<DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={rows} />
<DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={rows} isExpandable={true}/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this change intended? I think it can be reverted.

{(rowIsObject ? row.row : row).map((cell, colIndex) => {
const cellIsObject = isDataViewTdObject(cell);
return (
<Tbody key={rowIndex} isExpanded={isRowExpanded} onDragOver={onDragOver} onDrop={onDropTbody} onDragLeave={onDragLeave}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be tightened up a bit so that the separate <Tbody> wrapper should only apply when isExpandable || isDraggable is true.

I was tinkering with claude a bit and claude suggested something like the following can handle the conditional:

 const renderedRows = useMemo(() => {
   const needsSeparateTbody = isExpandable || isDraggable;

   return rows.map((row, rowIndex) => {
     // ... calculate row data ...

     if (needsSeparateTbody) {
       return (
         <Tbody key={rowIndex} isExpanded={isRowExpanded} ...>
           <Tr ...>{/* cells */}</Tr>
           {/* expandable content */}
         </Tbody>
       );
     } else {
       // Original structure for basic tables
       return (
         <Tr key={rowIndex} ...>
           {/* cells */}
         </Tr>
       );
     }
   });
 }, [...]);

 // And in the return:
 if (needsSeparateTbody) {
   return <Table ...>{activeBodyState || renderedRows}</Table>;
 } else {
   return <Table ...>{activeBodyState || <Tbody>{renderedRows}</Tbody>}</Table>;
 }

}
};

const onDrop: TrProps['onDrop'] = (evt) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The draggable rows feature currently reorders rows in the DOM but doesn't provide a way for parent components to be notified of the new order. This means users cannot persist the reordered state or update their data model.

Let's add an additional prop which is a callback that is executed when the order is updated. something like:

export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | 'rows'> {
    // ... existing props
    /** Callback fired when rows are reordered via drag and drop */
    onReorder?: (newOrder: string[], oldOrder: string[]) => void;
  }

then you would want to leverage that new prop in useDraggableRows:

export interface UseDraggableRowsProps {
    rows: DataViewTr[];
    tableRef: React.RefObject<HTMLTableElement>;
    onReorder?: (newOrder: string[], oldOrder: string[]) => void;
  }

  export const useDraggableRows = ({
    rows,
    tableRef,
    onReorder
  }: UseDraggableRowsProps): UseDraggableRowsReturn => {
    // ... existing code ...

    const onDrop: TrProps['onDrop'] = (evt) => {
      if (isValidDrop(evt) && tableRef.current) {
        const tbodyNodes = Array.from(tableRef.current.querySelectorAll('tbody'));
        const finalOrder = tbodyNodes
          .map(tbody => tbody.querySelector('tr[id]'))
          .filter((tr): tr is HTMLTableRowElement => tr !== null)
          .map(tr => tr.id);

        const oldOrder = itemOrder;
        setItemOrder(finalOrder);

        // Notify parent component
        if (onReorder && JSON.stringify(oldOrder) !== JSON.stringify(finalOrder)) {
          onReorder(finalOrder, oldOrder);
        }
      } else {
        onDragCancel();
      }
    };

    // ... rest of implementation
  };

isExpanded: isRowExpanded && expandedColIndex === colIndex,
expandId: `expandable-${rowIndex}`,
onToggle: () => {
console.log(`toggled compound expand for row ${rowIndex}, column ${colIndex}`); // eslint-disable-line no-console
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be removed?

export interface UseDraggableRowsProps {
rows: DataViewTr[];
tableRef: React.RefObject<HTMLTableElement>;
isDraggable?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used anywhere in this hook?


### 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*behaviour (or behavior)

}}
>
{cellIsObject ? cell.cell : cell}
<GripVerticalIcon />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to note that this icon needs an accessible label but realized that I don't think you should need to declare the icon directly.

can you reuse PF's table API to get the correct drag handle + behavior more out of the box?

something like:

{isDraggable && (
    <Td
      key={`drag-${rowIndex}`}
      draggableRow={{
        id: `draggable-row-${rowIds[rowIndex]}`
      }}
    />
  )}

i'm looking at https://www.patternfly.org/components/table/#draggable-row-table

may be worth seeing if there are more out of the box capaibilities you're not yet leveraging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants