From c931f21e24bb6638bdb76c7152de3f00c0357bac Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Tue, 10 Mar 2026 16:33:40 +0530 Subject: [PATCH 1/3] feat: add anchored header for groups --- apps/www/src/app/examples/datatable/page.tsx | 562 ++++++++++++++++++ .../docs/components/datatable/index.mdx | 16 + .../docs/components/datatable/props.ts | 7 + .../data-table/__tests__/data-table.test.tsx | 57 ++ .../data-table/components/content.tsx | 20 +- .../components/virtualized-content.tsx | 87 ++- .../data-table/data-table.module.css | 28 +- .../components/data-table/data-table.tsx | 9 +- .../data-table/data-table.types.tsx | 3 + 9 files changed, 769 insertions(+), 20 deletions(-) create mode 100644 apps/www/src/app/examples/datatable/page.tsx diff --git a/apps/www/src/app/examples/datatable/page.tsx b/apps/www/src/app/examples/datatable/page.tsx new file mode 100644 index 000000000..aa2662f73 --- /dev/null +++ b/apps/www/src/app/examples/datatable/page.tsx @@ -0,0 +1,562 @@ +'use client'; + +import { + Button, + DataTable, + EmptyState, + Flex, + IconButton, + Navbar, + Search, + Sidebar, + Text +} from '@raystack/apsara'; +import { BellIcon, FilterIcon, SidebarIcon } from '@raystack/apsara/icons'; +import { useState } from 'react'; + +const sampleData = [ + { + id: '1', + name: 'Alice', + email: 'alice@example.com', + role: 'Admin', + department: 'Engineering', + team: 'Frontend', + location: 'NYC', + phone: '+1-555-0101', + status: 'Active', + joined: '2022-01-15' + }, + { + id: '2', + name: 'Bob', + email: 'bob@example.com', + role: 'User', + department: 'Product', + team: 'Design', + location: 'SF', + phone: '+1-555-0102', + status: 'Active', + joined: '2022-03-20' + }, + { + id: '3', + name: 'Carol', + email: 'carol@example.com', + role: 'Manager', + department: 'Engineering', + team: 'Backend', + location: 'NYC', + phone: '+1-555-0103', + status: 'Active', + joined: '2021-11-08' + }, + { + id: '4', + name: 'Dave', + email: 'dave@example.com', + role: 'User', + department: 'Sales', + team: 'East', + location: 'Boston', + phone: '+1-555-0104', + status: 'Away', + joined: '2023-02-14' + }, + { + id: '5', + name: 'Eve', + email: 'eve@example.com', + role: 'Admin', + department: 'Engineering', + team: 'Frontend', + location: 'Remote', + phone: '+1-555-0105', + status: 'Active', + joined: '2020-06-01' + }, + { + id: '6', + name: 'Frank', + email: 'frank@example.com', + role: 'User', + department: 'Support', + team: 'Tier 1', + location: 'Austin', + phone: '+1-555-0106', + status: 'Active', + joined: '2023-05-10' + }, + { + id: '7', + name: 'Grace', + email: 'grace@example.com', + role: 'Manager', + department: 'Product', + team: 'Design', + location: 'SF', + phone: '+1-555-0107', + status: 'Active', + joined: '2021-09-22' + }, + { + id: '8', + name: 'Henry', + email: 'henry@example.com', + role: 'Admin', + department: 'Engineering', + team: 'Backend', + location: 'Seattle', + phone: '+1-555-0108', + status: 'Away', + joined: '2019-12-05' + }, + { + id: '9', + name: 'Ivy', + email: 'ivy@example.com', + role: 'User', + department: 'Marketing', + team: 'Content', + location: 'NYC', + phone: '+1-555-0109', + status: 'Active', + joined: '2022-08-30' + }, + { + id: '10', + name: 'Jack', + email: 'jack@example.com', + role: 'User', + department: 'Engineering', + team: 'Frontend', + location: 'Remote', + phone: '+1-555-0110', + status: 'Active', + joined: '2023-01-12' + }, + { + id: '11', + name: 'Kate', + email: 'kate@example.com', + role: 'Manager', + department: 'Sales', + team: 'West', + location: 'LA', + phone: '+1-555-0111', + status: 'Active', + joined: '2020-04-18' + }, + { + id: '12', + name: 'Leo', + email: 'leo@example.com', + role: 'Admin', + department: 'Engineering', + team: 'DevOps', + location: 'NYC', + phone: '+1-555-0112', + status: 'Active', + joined: '2021-07-07' + }, + { + id: '13', + name: 'Mia', + email: 'mia@example.com', + role: 'User', + department: 'Product', + team: 'Design', + location: 'Chicago', + phone: '+1-555-0113', + status: 'Away', + joined: '2022-11-25' + }, + { + id: '14', + name: 'Noah', + email: 'noah@example.com', + role: 'User', + department: 'Support', + team: 'Tier 2', + location: 'Austin', + phone: '+1-555-0114', + status: 'Active', + joined: '2023-03-03' + }, + { + id: '15', + name: 'Olivia', + email: 'olivia@example.com', + role: 'Manager', + department: 'Engineering', + team: 'Frontend', + location: 'SF', + phone: '+1-555-0115', + status: 'Active', + joined: '2020-10-11' + }, + { + id: '16', + name: 'Paul', + email: 'paul@example.com', + role: 'Admin', + department: 'Sales', + team: 'East', + location: 'Boston', + phone: '+1-555-0116', + status: 'Active', + joined: '2019-08-19' + }, + { + id: '17', + name: 'Quinn', + email: 'quinn@example.com', + role: 'User', + department: 'Marketing', + team: 'Growth', + location: 'Remote', + phone: '+1-555-0117', + status: 'Active', + joined: '2022-05-06' + }, + { + id: '18', + name: 'Ryan', + email: 'ryan@example.com', + role: 'User', + department: 'Engineering', + team: 'Backend', + location: 'Seattle', + phone: '+1-555-0118', + status: 'Away', + joined: '2021-02-28' + }, + { + id: '19', + name: 'Sara', + email: 'sara@example.com', + role: 'Manager', + department: 'Support', + team: 'Tier 1', + location: 'Austin', + phone: '+1-555-0119', + status: 'Active', + joined: '2020-01-14' + }, + { + id: '20', + name: 'Tom', + email: 'tom@example.com', + role: 'Admin', + department: 'Product', + team: 'Design', + location: 'NYC', + phone: '+1-555-0120', + status: 'Active', + joined: '2018-12-01' + }, + { + id: '21', + name: 'Uma', + email: 'uma@example.com', + role: 'User', + department: 'Engineering', + team: 'Frontend', + location: 'Remote', + phone: '+1-555-0121', + status: 'Active', + joined: '2023-04-17' + }, + { + id: '22', + name: 'Victor', + email: 'victor@example.com', + role: 'User', + department: 'Sales', + team: 'West', + location: 'LA', + phone: '+1-555-0122', + status: 'Active', + joined: '2022-09-09' + }, + { + id: '23', + name: 'Wendy', + email: 'wendy@example.com', + role: 'Manager', + department: 'Engineering', + team: 'Backend', + location: 'SF', + phone: '+1-555-0123', + status: 'Away', + joined: '2021-06-21' + }, + { + id: '24', + name: 'Xavier', + email: 'xavier@example.com', + role: 'Admin', + department: 'Marketing', + team: 'Content', + location: 'Chicago', + phone: '+1-555-0124', + status: 'Active', + joined: '2019-03-12' + }, + { + id: '25', + name: 'Yara', + email: 'yara@example.com', + role: 'User', + department: 'Product', + team: 'Design', + location: 'Remote', + phone: '+1-555-0125', + status: 'Active', + joined: '2022-07-04' + }, + { + id: '26', + name: 'Zane', + email: 'zane@example.com', + role: 'User', + department: 'Support', + team: 'Tier 2', + location: 'Austin', + phone: '+1-555-0126', + status: 'Active', + joined: '2023-02-22' + }, + { + id: '27', + name: 'Amy', + email: 'amy@example.com', + role: 'Manager', + department: 'Engineering', + team: 'DevOps', + location: 'NYC', + phone: '+1-555-0127', + status: 'Active', + joined: '2020-11-30' + }, + { + id: '28', + name: 'Ben', + email: 'ben@example.com', + role: 'Admin', + department: 'Sales', + team: 'East', + location: 'Boston', + phone: '+1-555-0128', + status: 'Away', + joined: '2021-04-05' + }, + { + id: '29', + name: 'Chloe', + email: 'chloe@example.com', + role: 'User', + department: 'Marketing', + team: 'Growth', + location: 'SF', + phone: '+1-555-0129', + status: 'Active', + joined: '2022-12-19' + }, + { + id: '30', + name: 'Dan', + email: 'dan@example.com', + role: 'User', + department: 'Engineering', + team: 'Frontend', + location: 'Seattle', + phone: '+1-555-0130', + status: 'Active', + joined: '2023-06-08' + } +]; + +const columns = [ + { + accessorKey: 'name', + header: 'Name', + enableColumnFilter: true, + filterType: 'string' as const, + enableGrouping: true + }, + { + accessorKey: 'email', + header: 'Email', + enableColumnFilter: true, + filterType: 'string' as const + }, + { + accessorKey: 'role', + header: 'Role', + enableColumnFilter: true, + filterType: 'select' as const, + enableGrouping: true, + showGroupCount: true, + filterOptions: [ + { value: 'Admin', label: 'Admin' }, + { value: 'User', label: 'User' }, + { value: 'Manager', label: 'Manager' } + ] + }, + { accessorKey: 'department', header: 'Department' }, + { accessorKey: 'team', header: 'Team' }, + { accessorKey: 'location', header: 'Location' }, + { accessorKey: 'phone', header: 'Phone' }, + { accessorKey: 'status', header: 'Status' }, + { accessorKey: 'joined', header: 'Joined' }, + { accessorKey: 'name', id: 'name_2', header: 'Name (2)' }, + { accessorKey: 'email', id: 'email_2', header: 'Email (2)' }, + { accessorKey: 'role', id: 'role_2', header: 'Role (2)' }, + { accessorKey: 'department', id: 'dept_2', header: 'Department (2)' }, + { accessorKey: 'team', id: 'team_2', header: 'Team (2)' }, + { accessorKey: 'location', id: 'loc_2', header: 'Location (2)' }, + { accessorKey: 'phone', id: 'phone_2', header: 'Phone (2)' }, + { accessorKey: 'status', id: 'status_2', header: 'Status (2)' }, + { accessorKey: 'joined', id: 'joined_2', header: 'Joined (2)' }, + { accessorKey: 'name', id: 'name_3', header: 'Name (3)' }, + { accessorKey: 'email', id: 'email_3', header: 'Email (3)' }, + { accessorKey: 'role', id: 'role_3', header: 'Role (3)' } +]; + +const PAGE_SIZE = 10; +const MAX_ROWS = 50; + +const Page = () => { + const [navbarSearch, setNavbarSearch] = useState(''); + const [data, setData] = useState(() => sampleData.slice(0, PAGE_SIZE)); + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(true); + + const loadMore = (): Promise => { + if (loading || !hasMore) return Promise.resolve(); + setLoading(true); + return new Promise(resolve => { + setTimeout(() => { + setData(prev => { + const next = sampleData.slice(prev.length, prev.length + PAGE_SIZE); + if (next.length < PAGE_SIZE) setHasMore(false); + return prev.length + next.length <= MAX_ROWS + ? [...prev, ...next] + : prev; + }); + setLoading(false); + resolve(); + }, 500); + }); + }; + + return ( + + + + + {}} aria-label='Logo'> + + + + Raystack + + + + + }> + Examples + + } + > + DataTable + + }> + Reports + Activities + + + Settings + Notifications + + + + Help & Support + Preferences + + + + + + + + DataTable – Infinite scroll + + + + ) => + setNavbarSearch(e.target.value) + } + onClear={() => setNavbarSearch('')} + size='small' + style={{ width: '200px' }} + /> + + + + + + + + + } + heading='No results' + variant='empty1' + subHeading='Try adjusting your filters or search.' + /> + } + /> + + + + + ); +}; + +export default Page; diff --git a/apps/www/src/content/docs/components/datatable/index.mdx b/apps/www/src/content/docs/components/datatable/index.mdx index 0dcf6b202..d74f00abf 100644 --- a/apps/www/src/content/docs/components/datatable/index.mdx +++ b/apps/www/src/content/docs/components/datatable/index.mdx @@ -210,6 +210,22 @@ const columns = [ ]; ``` +#### Anchor group title + +When grouping is enabled, you can make the current group label stick under the table header while scrolling (anchor group title) in both `DataTable.Content` and `DataTable.VirtualizedContent` by setting `stickyGroupHeader={true}` on the root. It is off by default. + +```tsx + + + + +``` + ### Server-side Integration ```tsx diff --git a/apps/www/src/content/docs/components/datatable/props.ts b/apps/www/src/content/docs/components/datatable/props.ts index 4164fed6b..d9a1ec2ef 100644 --- a/apps/www/src/content/docs/components/datatable/props.ts +++ b/apps/www/src/content/docs/components/datatable/props.ts @@ -35,6 +35,13 @@ export interface DataTableProps { onColumnVisibilityChange?: ( columnVisibility: Record ) => void; + + /** + * When true (default), the current group label sticks under the table header while scrolling (anchor group title). + * Applies to both Content and VirtualizedContent when grouping is enabled. + * @defaultValue false + */ + stickyGroupHeader?: boolean; } export interface DataTableQuery { diff --git a/packages/raystack/components/data-table/__tests__/data-table.test.tsx b/packages/raystack/components/data-table/__tests__/data-table.test.tsx index c359dca2a..b35f48ac4 100644 --- a/packages/raystack/components/data-table/__tests__/data-table.test.tsx +++ b/packages/raystack/components/data-table/__tests__/data-table.test.tsx @@ -575,4 +575,61 @@ describe('DataTable', () => { expect(screen.queryByTestId('zero-state')).not.toBeInTheDocument(); }); }); + + describe('stickyGroupHeader (anchor group title)', () => { + const columnsWithGrouping: DataTableColumnDef[] = [ + { + id: 'name', + accessorKey: 'name', + header: 'Name', + cell: ({ getValue }) => getValue(), + enableGrouping: true + }, + { + id: 'status', + accessorKey: 'status', + header: 'Status', + cell: ({ getValue }) => getValue(), + enableGrouping: true + } + ]; + + it('applies sticky class to group section header when stickyGroupHeader is true', () => { + const { container } = render( + + + + ); + + const sectionHeaderCell = container.querySelector( + `th.${styles.stickySectionHeader}` + ); + expect(sectionHeaderCell).toBeInTheDocument(); + }); + + it('does not apply sticky class when stickyGroupHeader is false (default)', () => { + const { container } = render( + + + + ); + + const sectionHeaderCell = container.querySelector( + `th.${styles.stickySectionHeader}` + ); + expect(sectionHeaderCell).not.toBeInTheDocument(); + }); + }); }); diff --git a/packages/raystack/components/data-table/components/content.tsx b/packages/raystack/components/data-table/components/content.tsx index 4cc3d3e66..1be431951 100644 --- a/packages/raystack/components/data-table/components/content.tsx +++ b/packages/raystack/components/data-table/components/content.tsx @@ -60,6 +60,7 @@ interface RowsProps { row?: string; }; lastRowRef?: React.RefObject; + stickyGroupHeader?: boolean; } function LoaderRows({ @@ -86,13 +87,20 @@ function LoaderRows({ function GroupHeader({ colSpan, - data + data, + stickySectionHeader }: { colSpan: number; data: GroupedData; + stickySectionHeader?: boolean; }) { return ( - + {data?.label} {data.showGroupCount ? ( @@ -107,7 +115,8 @@ function Rows({ rows = [], onRowClick, classNames, - lastRowRef + lastRowRef, + stickyGroupHeader = false }: RowsProps) { return rows.map((row, idx) => { const isSelected = row.getIsSelected(); @@ -121,6 +130,7 @@ function Rows({ key={row.id} colSpan={cells.length} data={row.original as GroupedData} + stickySectionHeader={stickyGroupHeader} /> ); } @@ -174,7 +184,8 @@ export function Content({ loadMoreData, loadingRowCount = 3, tableQuery, - defaultSort + defaultSort, + stickyGroupHeader = false } = useDataTable(); const headerGroups = table?.getHeaderGroups(); @@ -248,6 +259,7 @@ export function Content({ classNames={{ row: classNames.row }} + stickyGroupHeader={stickyGroupHeader} /> {isLoading ? ( (null); + const headerRef = useRef(null); + const [stickyGroup, setStickyGroup] = useState | null>( + null + ); + const [headerHeight, setHeaderHeight] = useState(40); + + const groupBy = tableQuery?.group_by?.[0]; + const isGrouped = Boolean(groupBy) && groupBy !== defaultGroupOption.id; + + const groupHeaderList = useMemo(() => { + const list: { index: number; data: GroupedData }[] = []; + rows.forEach((row, i) => { + if (row.subRows && row.subRows.length > 0) { + list.push({ index: i, data: row.original as GroupedData }); + } + }); + return list; + }, [rows]); const showLoaderRows = isLoading && rows.length > 0; @@ -244,17 +264,48 @@ export function VirtualizedContent({ overscan }); + const updateStickyGroup = useCallback(() => { + if (!stickyGroupHeader || !isGrouped || groupHeaderList.length === 0) { + setStickyGroup(null); + return; + } + const items = virtualizer.getVirtualItems(); + const firstIndex = items[0]?.index ?? 0; + const current = groupHeaderList + .filter(g => g.index <= firstIndex) + .pop()?.data; + setStickyGroup(current ?? null); + }, [stickyGroupHeader, isGrouped, groupHeaderList, virtualizer]); + const handleVirtualScroll = useCallback(() => { - if (!scrollContainerRef.current || isLoading) return; - const { scrollTop, scrollHeight, clientHeight } = - scrollContainerRef.current; + const el = scrollContainerRef.current; + if (!el) return; + if (stickyGroupHeader) updateStickyGroup(); + if (isLoading) return; + const { scrollTop, scrollHeight, clientHeight } = el; if (scrollHeight - scrollTop - clientHeight < loadMoreOffset) { loadMoreData(); } - }, [isLoading, loadMoreData, loadMoreOffset]); + }, [ + stickyGroupHeader, + isLoading, + loadMoreData, + loadMoreOffset, + updateStickyGroup + ]); const totalHeight = virtualizer.getTotalSize(); + useLayoutEffect(() => { + if (headerRef.current) { + setHeaderHeight(headerRef.current.getBoundingClientRect().height); + } + }, [headerGroups]); + + useLayoutEffect(() => { + if (stickyGroupHeader) updateStickyGroup(); + }, [stickyGroupHeader, updateStickyGroup, groupHeaderList, isGrouped]); + const hasData = rows?.length > 0 || isLoading; const hasChanges = hasActiveQuery(tableQuery || {}, defaultSort); @@ -281,10 +332,26 @@ export function VirtualizedContent({ onScroll={handleVirtualScroll} >
- +
+ +
+ {stickyGroupHeader && isGrouped && stickyGroup && ( +
+ + {stickyGroup.label} + {stickyGroup.showGroupCount ? ( + {stickyGroup.count} + ) : null} + +
+ )}
span { +.display-popover-properties-select>span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -145,6 +145,19 @@ padding: var(--rs-space-3) var(--rs-space-4); } +/* Sticky group anchor: shows current group label while scrolling (virtualized) */ +.stickyGroupAnchor { + position: sticky; + z-index: 1; + display: flex; + align-items: center; + background: var(--rs-color-background-base-secondary); + font-weight: 500; + padding: var(--rs-space-3); + border-bottom: 0.5px solid var(--rs-color-border-base-primary); + box-shadow: 0 1px 0 0 var(--rs-color-border-base-primary); +} + .stickyLoaderContainer { position: sticky; bottom: 0; @@ -155,3 +168,12 @@ .loaderRow { position: relative; } + +/* Non-virtualized: sticky section header under table header */ +.stickySectionHeader { + position: sticky; + top: var(--rs-space-10); + z-index: 1; + background: var(--rs-color-background-base-secondary); + box-shadow: 0 1px 0 0 var(--rs-color-border-base-primary); +} \ No newline at end of file diff --git a/packages/raystack/components/data-table/data-table.tsx b/packages/raystack/components/data-table/data-table.tsx index 1ac060131..058a5ab41 100644 --- a/packages/raystack/components/data-table/data-table.tsx +++ b/packages/raystack/components/data-table/data-table.tsx @@ -47,7 +47,8 @@ function DataTableRoot({ onTableQueryChange, onLoadMore, onRowClick, - onColumnVisibilityChange + onColumnVisibilityChange, + stickyGroupHeader = false }: React.PropsWithChildren>) { const defaultTableQuery = useMemo( () => getDefaultTableQuery(defaultSort, query), @@ -193,7 +194,8 @@ function DataTableRoot({ defaultSort, loadingRowCount, onRowClick, - shouldShowFilters + shouldShowFilters, + stickyGroupHeader }; }, [ table, @@ -207,7 +209,8 @@ function DataTableRoot({ defaultSort, loadingRowCount, onRowClick, - shouldShowFilters + shouldShowFilters, + stickyGroupHeader ]); return ( diff --git a/packages/raystack/components/data-table/data-table.types.tsx b/packages/raystack/components/data-table/data-table.types.tsx index 4fc47105a..2182faa6d 100644 --- a/packages/raystack/components/data-table/data-table.types.tsx +++ b/packages/raystack/components/data-table/data-table.types.tsx @@ -112,6 +112,8 @@ export interface DataTableProps { onLoadMore?: () => Promise; onRowClick?: (row: TData) => void; onColumnVisibilityChange?: (columnVisibility: VisibilityState) => void; + /** When true, group headers stick under the table header while scrolling. Default is false. */ + stickyGroupHeader?: boolean; } export type DataTableContentClassNames = { @@ -156,6 +158,7 @@ export type TableContextType = { updateTableQuery: (fn: TableQueryUpdateFn) => void; onRowClick?: (row: TData) => void; shouldShowFilters?: boolean; + stickyGroupHeader?: boolean; }; export interface ColumnData { From a51316576436b2293448a990758370df0ab03ee3 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Tue, 10 Mar 2026 20:04:22 +0530 Subject: [PATCH 2/3] fix: typo --- packages/raystack/components/sidebar/sidebar-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/raystack/components/sidebar/sidebar-item.tsx b/packages/raystack/components/sidebar/sidebar-item.tsx index a5e4a96eb..85d70bd6a 100644 --- a/packages/raystack/components/sidebar/sidebar-item.tsx +++ b/packages/raystack/components/sidebar/sidebar-item.tsx @@ -53,7 +53,7 @@ export const SidebarItem = forwardRef( align: 'center', gap: 3, className: cx(styles['nav-leading-icon'], classNames?.leadingIcon), - ariaHidden: true + 'aria-hidden': true } as const; const content = cloneElement( From da3b152bde01f0673afe91289176486f363bd8bc Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 12 Mar 2026 12:54:01 +0530 Subject: [PATCH 3/3] chore: missing comma and docs typo --- apps/www/src/content/docs/components/datatable/props.ts | 4 ++-- .../components/data-table/__tests__/data-table.test.tsx | 7 ++----- packages/raystack/components/data-table/data-table.tsx | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/www/src/content/docs/components/datatable/props.ts b/apps/www/src/content/docs/components/datatable/props.ts index d985c9513..ee8cb3e08 100644 --- a/apps/www/src/content/docs/components/datatable/props.ts +++ b/apps/www/src/content/docs/components/datatable/props.ts @@ -37,12 +37,12 @@ export interface DataTableProps { ) => void; /** - * When true (default), the current group label sticks under the table header while scrolling (anchor group title). + * When true, the current group label sticks under the table header while scrolling (anchor group title). * Applies to both Content and VirtualizedContent when grouping is enabled. * @defaultValue false */ stickyGroupHeader?: boolean; - + /** * Return a stable unique id for each row (used as React key). * Use for sortable/filterable tables to avoid key issues when rows reorder. diff --git a/packages/raystack/components/data-table/__tests__/data-table.test.tsx b/packages/raystack/components/data-table/__tests__/data-table.test.tsx index 6c6cbae57..daeedfb54 100644 --- a/packages/raystack/components/data-table/__tests__/data-table.test.tsx +++ b/packages/raystack/components/data-table/__tests__/data-table.test.tsx @@ -588,11 +588,8 @@ describe('DataTable', () => { ); - - const sectionHeaderCell = container.querySelector( - `th.${styles.stickySectionHeader}` - ); - expect(sectionHeaderCell).not.toBeInTheDocument(); + expect(screen.getByRole('table')).toBeInTheDocument(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); }); }); }); diff --git a/packages/raystack/components/data-table/data-table.tsx b/packages/raystack/components/data-table/data-table.tsx index eaff2241a..9588d59a2 100644 --- a/packages/raystack/components/data-table/data-table.tsx +++ b/packages/raystack/components/data-table/data-table.tsx @@ -48,7 +48,7 @@ function DataTableRoot({ onLoadMore, onRowClick, onColumnVisibilityChange, - stickyGroupHeader = false + stickyGroupHeader = false, getRowId }: React.PropsWithChildren>) { const defaultTableQuery = useMemo(