From 385b48c0c7767f0908e0ec98a56a68fadf011310 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Fri, 3 Oct 2025 11:49:36 -0400 Subject: [PATCH] fix(SkeletonTableHead): prevent nested th elements when passing Th components When React elements (like ) were passed in the columns prop, they were being wrapped in an additional element, causing invalid DOM structure with nested elements. This fix checks if a column is already a React element and renders it directly without wrapping it in an additional Th component. Fixes #764 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../SkeletonTableHead.test.tsx | 26 ++++- .../SkeletonTableHead/SkeletonTableHead.tsx | 33 ++++-- .../SkeletonTableHead.test.tsx.snap | 101 +++++++++++++++++- 3 files changed, 149 insertions(+), 11 deletions(-) diff --git a/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx b/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx index ae81e3ad..5daaf3a1 100644 --- a/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx +++ b/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx @@ -1,13 +1,35 @@ import { render } from '@testing-library/react'; import SkeletonTableHead from './SkeletonTableHead'; -import { Table } from '@patternfly/react-table'; +import { Table, Th } from '@patternfly/react-table'; describe('SkeletonTableHead component', () => { it('should render correctly with count', () => { expect(render(
)).toMatchSnapshot(); }); - it('should render correctly with count', () => { + it('should render correctly with string columns', () => { expect(render(
)).toMatchSnapshot(); }); + + it('should render correctly with Th element columns without nesting', () => { + const { container } = render( + + First, + + ]} + /> +
Second
+ ); + + // Ensure there are no nested elements + const theadCells = container.querySelectorAll('thead th'); + theadCells.forEach(th => { + const nestedTh = th.querySelector('th'); + expect(nestedTh).toBeNull(); + }); + + expect(container).toMatchSnapshot(); + }); }); diff --git a/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx b/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx index 7cacc135..907caf1b 100644 --- a/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx +++ b/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx @@ -1,5 +1,5 @@ -import type { FunctionComponent } from 'react'; -import { ReactNode, useMemo } from 'react'; +import type { FunctionComponent, ReactElement } from 'react'; +import { ReactNode, useMemo, isValidElement } from 'react'; import { Th, ThProps, @@ -10,6 +10,8 @@ import { Skeleton } from '@patternfly/react-core'; export const isThObject = (value: ReactNode | { cell: ReactNode; props?: ThProps }): value is { cell: ReactNode; props?: ThProps } => value != null && typeof value === 'object' && 'cell' in value; +export const isReactElement = (value: ReactNode): value is ReactElement => isValidElement(value); + export interface SkeletonTableHeadProps { /** Custom columns for the table */ columns?: (ReactNode | { cell: ReactNode; props?: ThProps })[]; @@ -41,18 +43,33 @@ export const SkeletonTableHead: FunctionComponent = ({ ...(isExpandable ? [ ] : []), ...(isSelectable && !isTreeTable ? [ ] : []), ...(hasCustomColumns ? ( - columns.map((column, index) => ( - - {isThObject(column) ? column.cell : column} - - )) + columns.map((column, index) => { + // If the column is an object with cell and props, wrap in Th + if (isThObject(column)) { + return ( + + {column.cell} + + ); + } + // If the column is already a React element (like ), render it directly + if (isReactElement(column)) { + return column; + } + // Otherwise, wrap the content in Th + return ( + + {column} + + ); + }) ) : ( [ ...Array(rowCellsCount) ].map((_, index) => ( )) - )) + )) ] , [ hasCustomColumns, isExpandable, isSelectable, isTreeTable, columns, rowCellsCount, ouiaId ]); diff --git a/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap b/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap index 257d1fcb..399a9d7d 100644 --- a/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap +++ b/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap @@ -1,5 +1,104 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`SkeletonTableHead component should render correctly with Th element columns without nesting 1`] = ` +
+ + + + + + + +
+ + + +
+
+`; + exports[`SkeletonTableHead component should render correctly with count 1`] = ` { "asFragment": [Function], @@ -203,7 +302,7 @@ exports[`SkeletonTableHead component should render correctly with count 1`] = ` } `; -exports[`SkeletonTableHead component should render correctly with count 2`] = ` +exports[`SkeletonTableHead component should render correctly with string columns 1`] = ` { "asFragment": [Function], "baseElement":