@indico-data/design-system 2.47.3 → 2.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/lib/components/index.d.ts +1 -0
  2. package/lib/components/pagination/Pagination.d.ts +2 -0
  3. package/lib/components/pagination/Pagination.stories.d.ts +6 -0
  4. package/lib/components/pagination/__tests__/Pagination.test.d.ts +1 -0
  5. package/lib/components/pagination/index.d.ts +1 -0
  6. package/lib/components/pagination/types.d.ts +6 -0
  7. package/lib/components/table/Table.stories.d.ts +1 -0
  8. package/lib/components/table/__tests__/Table.test.d.ts +1 -0
  9. package/lib/components/table/components/HorizontalStickyHeader.d.ts +10 -0
  10. package/lib/components/table/components/TablePagination.d.ts +9 -0
  11. package/lib/components/table/components/__tests__/HorizontalStickyHeader.test.d.ts +1 -0
  12. package/lib/components/table/components/__tests__/TablePagination.test.d.ts +1 -0
  13. package/lib/components/table/components/helpers.d.ts +6 -0
  14. package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +8 -0
  15. package/lib/components/table/sampleData.d.ts +6 -0
  16. package/lib/components/table/types.d.ts +16 -5
  17. package/lib/components/table/utils/processColumns.d.ts +2 -0
  18. package/lib/index.css +78 -17
  19. package/lib/index.d.ts +16 -5
  20. package/lib/index.esm.css +78 -17
  21. package/lib/index.esm.js +305 -14
  22. package/lib/index.esm.js.map +1 -1
  23. package/lib/index.js +304 -13
  24. package/lib/index.js.map +1 -1
  25. package/lib/utils/getPreviousHeadersWidth.d.ts +1 -0
  26. package/package.json +1 -1
  27. package/src/components/index.ts +1 -0
  28. package/src/components/pagination/Pagination.mdx +31 -0
  29. package/src/components/pagination/Pagination.stories.tsx +80 -0
  30. package/src/components/pagination/Pagination.tsx +117 -0
  31. package/src/components/pagination/__tests__/Pagination.test.tsx +91 -0
  32. package/src/components/pagination/index.ts +1 -0
  33. package/src/components/pagination/styles/Pagination.scss +22 -0
  34. package/src/components/pagination/types.ts +6 -0
  35. package/src/components/table/Table.mdx +136 -0
  36. package/src/components/table/Table.stories.tsx +91 -30
  37. package/src/components/table/Table.tsx +25 -2
  38. package/src/components/table/__tests__/Table.test.tsx +10 -0
  39. package/src/components/table/components/HorizontalStickyHeader.tsx +57 -0
  40. package/src/components/table/components/TablePagination.tsx +44 -0
  41. package/src/components/table/components/__tests__/HorizontalStickyHeader.test.tsx +104 -0
  42. package/src/components/table/components/__tests__/TablePagination.test.tsx +17 -0
  43. package/src/components/table/components/helpers.ts +90 -0
  44. package/src/components/table/hooks/usePinnedColumnsManager.ts +146 -0
  45. package/src/components/table/sampleData.tsx +436 -0
  46. package/src/components/table/styles/Table.scss +72 -24
  47. package/src/components/table/styles/_variables.scss +3 -0
  48. package/src/components/table/types.ts +19 -7
  49. package/src/components/table/utils/processColumns.tsx +35 -0
  50. package/src/setup/setupIcons.ts +4 -0
  51. package/src/setup/setupTests.ts +8 -0
  52. package/src/styles/index.scss +1 -0
  53. package/src/utils/getPreviousHeadersWidth.ts +12 -0
  54. package/src/components/table/sampleData.ts +0 -171
@@ -3,9 +3,10 @@ import DataTable, {
3
3
  Direction as RDTDirection,
4
4
  Alignment as RDTAlignment,
5
5
  } from 'react-data-table-component';
6
-
7
6
  import { LoadingComponent } from './LoadingComponent';
8
7
  import { TableProps } from './types';
8
+ import { TablePagination } from './components/TablePagination';
9
+ import { usePinnedColumnsManager } from './hooks/usePinnedColumnsManager';
9
10
 
10
11
  export const Table = <T,>(props: TableProps<T>) => {
11
12
  const {
@@ -19,9 +20,22 @@ export const Table = <T,>(props: TableProps<T>) => {
19
20
  isFullHeight = false,
20
21
  subHeaderAlign = 'left',
21
22
  className,
23
+ paginationTotalRows,
24
+ totalEntriesText,
25
+ data,
26
+ columns: initialColumns,
27
+ canPinColumns = false,
28
+ onPinnedColumnsChange,
22
29
  ...rest
23
30
  } = props;
24
31
 
32
+ // Turns on/off column pinning.
33
+ const { columnsWithPinning } = usePinnedColumnsManager(
34
+ initialColumns,
35
+ canPinColumns,
36
+ onPinnedColumnsChange,
37
+ );
38
+
25
39
  const combinedClassName = classNames(className, {
26
40
  'table--striped': striped,
27
41
  'table-body': true,
@@ -32,8 +46,10 @@ export const Table = <T,>(props: TableProps<T>) => {
32
46
  });
33
47
 
34
48
  return (
35
- <div className={tableWrapperClassName}>
49
+ <div className={tableWrapperClassName} data-testid="table">
36
50
  <DataTable
51
+ data={data}
52
+ columns={columnsWithPinning}
37
53
  responsive={responsive}
38
54
  direction={direction as RDTDirection}
39
55
  subHeaderAlign={subHeaderAlign as RDTAlignment}
@@ -44,6 +60,13 @@ export const Table = <T,>(props: TableProps<T>) => {
44
60
  noDataComponent={noDataComponent}
45
61
  progressPending={isLoading}
46
62
  progressComponent={<LoadingComponent />}
63
+ pagination
64
+ paginationComponent={(props) => (
65
+ <TablePagination {...props} totalEntriesText={totalEntriesText} />
66
+ )}
67
+ paginationTotalRows={paginationTotalRows}
68
+ highlightOnHover
69
+ pointerOnHover
47
70
  {...rest}
48
71
  />
49
72
  </div>
@@ -0,0 +1,10 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { Table } from '../Table';
3
+ import { sampleData, columns } from '../sampleData';
4
+
5
+ describe('Table', () => {
6
+ it('renders the total entries text', () => {
7
+ render(<Table columns={columns} data={sampleData} totalEntriesText="100 entries" />);
8
+ expect(screen.getByTestId('table-pagination-total-entries')).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,57 @@
1
+ import { useEffect } from 'react';
2
+ import { Button } from '../../button/Button';
3
+ import {
4
+ getPreviousHeadersWidth,
5
+ applyStickyStylesToTableHeader,
6
+ clearStickyStyles,
7
+ } from './helpers';
8
+ interface HorizontalStickyHeaderProps {
9
+ children: React.ReactNode;
10
+ position: number;
11
+ onPinColumn?: (columnId: string) => void;
12
+ isPinned?: boolean;
13
+ forceUpdate?: number;
14
+ pinnedColumnIds: string[];
15
+ }
16
+
17
+ const HorizontalStickyHeader = ({
18
+ children,
19
+ position,
20
+ onPinColumn,
21
+ isPinned = false,
22
+ pinnedColumnIds,
23
+ }: HorizontalStickyHeaderProps) => {
24
+ useEffect(() => {
25
+ const calculateWidth = async () => {
26
+ await new Promise((resolve) => setTimeout(resolve, 0));
27
+ const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
28
+ if (header) {
29
+ if (isPinned) {
30
+ const width = getPreviousHeadersWidth(position, pinnedColumnIds);
31
+ await applyStickyStylesToTableHeader(position, width);
32
+ } else {
33
+ clearStickyStyles(header as HTMLElement);
34
+ }
35
+ }
36
+ };
37
+
38
+ calculateWidth();
39
+ }, [position, isPinned, pinnedColumnIds]);
40
+
41
+ return (
42
+ <div className="table__header-cell" data-testid={`sticky-column-${position}`}>
43
+ <Button
44
+ data-testid={`sticky-header-pin-button-${position}`}
45
+ variant="link"
46
+ size="sm"
47
+ iconLeft="pin"
48
+ onClick={onPinColumn}
49
+ ariaLabel={isPinned ? 'Unpin column' : 'Pin column'}
50
+ className={`table__column--${isPinned ? 'is-pinned' : 'is-not-pinned'} table__column__pin-action`}
51
+ />
52
+ <div className="table__header-content">{children}</div>
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default HorizontalStickyHeader;
@@ -0,0 +1,44 @@
1
+ import { Pagination as PaginationComponent } from '../../pagination';
2
+ import { Row, Col } from '../../grid';
3
+ interface TablePaginationProps {
4
+ rowsPerPage: number;
5
+ rowCount: number;
6
+ onChangePage: (page: number, perPage: number) => void;
7
+ currentPage: number;
8
+ totalEntriesText?: string;
9
+ }
10
+
11
+ export const TablePagination = ({
12
+ rowsPerPage,
13
+ rowCount,
14
+ onChangePage,
15
+ currentPage,
16
+ totalEntriesText,
17
+ }: TablePaginationProps) => {
18
+ const totalPages = Math.ceil(rowCount / rowsPerPage);
19
+
20
+ return (
21
+ <div className="table__pagination">
22
+ <Row align="center" justify="between">
23
+ <Col xs="content">
24
+ {totalEntriesText && (
25
+ <span
26
+ data-testid="table-pagination-total-entries"
27
+ className="table__pagination-total-entries"
28
+ >
29
+ {totalEntriesText}
30
+ </span>
31
+ )}
32
+ </Col>
33
+ <Col xs="content">
34
+ <PaginationComponent
35
+ data-testid="table-pagination-component"
36
+ totalPages={totalPages}
37
+ currentPage={currentPage}
38
+ onChange={(page) => onChangePage(page, rowsPerPage)}
39
+ />
40
+ </Col>
41
+ </Row>
42
+ </div>
43
+ );
44
+ };
@@ -0,0 +1,104 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import HorizontalStickyHeader from '../HorizontalStickyHeader';
3
+ import { sampleData } from '../../sampleData';
4
+ import { Table } from '../../Table';
5
+ import { columns } from '../../sampleData';
6
+ import userEvent from '@testing-library/user-event';
7
+
8
+ // Add ResizeObserver mock before tests
9
+ class ResizeObserverMock {
10
+ observe() {}
11
+ unobserve() {}
12
+ disconnect() {}
13
+ }
14
+
15
+ // Add the mock to the global object
16
+ global.ResizeObserver = ResizeObserverMock;
17
+
18
+ describe('HorizontalStickyHeader', () => {
19
+ // Store original implementation
20
+ const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
21
+
22
+ afterEach(() => {
23
+ // Restore original implementation after each test
24
+ Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
25
+ });
26
+
27
+ it('correctly handles column pinning states based on column configuration', () => {
28
+ render(
29
+ <Table
30
+ responsive
31
+ columns={columns}
32
+ canPinColumns
33
+ data={sampleData}
34
+ totalEntriesText="100 entries"
35
+ />,
36
+ );
37
+
38
+ columns.forEach((column, index) => {
39
+ if (column.isPinned === undefined) {
40
+ // For columns where isPinned is not defined, the pin button should not exist
41
+ const pinButton = screen.queryByTestId(`sticky-header-pin-button-${index}`);
42
+ expect(pinButton).not.toBeInTheDocument();
43
+ } else {
44
+ // For columns where isPinned is defined, check for the correct class
45
+ const pinButton = screen.getByTestId(`sticky-header-pin-button-${index}`);
46
+ expect(pinButton).toHaveClass(
47
+ column.isPinned ? 'table__column--is-pinned' : 'table__column--is-not-pinned',
48
+ );
49
+ }
50
+ });
51
+ });
52
+
53
+ it('fires the onPinColumn callback when the pin button is clicked', () => {
54
+ const onPinColumn = jest.fn();
55
+ render(
56
+ <HorizontalStickyHeader position={0} pinnedColumnIds={[]} onPinColumn={onPinColumn}>
57
+ Test Content
58
+ </HorizontalStickyHeader>,
59
+ );
60
+
61
+ const pinButton = screen.getByTestId('sticky-header-pin-button-0');
62
+ fireEvent.click(pinButton);
63
+ expect(onPinColumn).toHaveBeenCalled();
64
+ });
65
+
66
+ it('correctly handles column pinning states based on column configuration', () => {
67
+ render(
68
+ <Table
69
+ responsive
70
+ columns={columns}
71
+ canPinColumns
72
+ data={sampleData}
73
+ totalEntriesText="100 entries"
74
+ />,
75
+ );
76
+
77
+ columns.forEach((column, index) => {
78
+ if (column.isPinned === undefined) {
79
+ // For columns where isPinned is not defined, the pin button should not exist
80
+ const pinButton = screen.queryByTestId(`sticky-header-pin-button-${index}`);
81
+ expect(pinButton).not.toBeInTheDocument();
82
+ } else {
83
+ // For columns where isPinned is defined, check for the correct class
84
+ const pinButton = screen.getByTestId(`sticky-header-pin-button-${index}`);
85
+ expect(pinButton).toHaveClass(
86
+ column.isPinned ? 'table__column--is-pinned' : 'table__column--is-not-pinned',
87
+ );
88
+ }
89
+ });
90
+ });
91
+
92
+ it('fires the onPinColumn callback when the pin button is clicked', () => {
93
+ const onPinColumn = jest.fn();
94
+ render(
95
+ <HorizontalStickyHeader position={0} pinnedColumnIds={[]} onPinColumn={onPinColumn}>
96
+ Test Content
97
+ </HorizontalStickyHeader>,
98
+ );
99
+
100
+ const pinButton = screen.getByTestId('sticky-header-pin-button-0');
101
+ fireEvent.click(pinButton);
102
+ expect(onPinColumn).toHaveBeenCalled();
103
+ });
104
+ });
@@ -0,0 +1,17 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { TablePagination } from '../TablePagination';
3
+
4
+ describe('TablePagination', () => {
5
+ it('renders total entries text', () => {
6
+ render(
7
+ <TablePagination
8
+ rowsPerPage={10}
9
+ rowCount={100}
10
+ onChangePage={() => {}}
11
+ currentPage={1}
12
+ totalEntriesText="100 entries"
13
+ />,
14
+ );
15
+ expect(screen.getByTestId('table-pagination-total-entries')).toBeInTheDocument();
16
+ });
17
+ });
@@ -0,0 +1,90 @@
1
+ import { CSSObject } from 'styled-components';
2
+
3
+ // Gets the width of the previous pinned columns
4
+ export const getPreviousHeadersWidth = (position: number, pinnedColumnIds: string[]): number => {
5
+ let totalWidth = 0;
6
+
7
+ // Add checkbox column width if it's pinned
8
+ if (pinnedColumnIds.includes('checkbox-column')) {
9
+ const checkboxCell = document.querySelector('.rdt_TableCol:not([data-column-id])');
10
+ if (checkboxCell) {
11
+ totalWidth += (checkboxCell as HTMLElement).offsetWidth;
12
+ }
13
+ }
14
+
15
+ // Add widths of other pinned columns before this position
16
+ const previousHeaders = Array.from({ length: position }, (_, i) => {
17
+ const header = document.querySelector(`[data-column-id="sticky-column-${i}"]`);
18
+ if (header && pinnedColumnIds.includes(`sticky-column-${i}`)) {
19
+ return header as HTMLElement;
20
+ }
21
+ return null;
22
+ }).filter((header): header is HTMLElement => header !== null);
23
+
24
+ // Calculate base width from previous columns
25
+ totalWidth = previousHeaders.reduce((acc, header) => {
26
+ return acc + header.offsetWidth;
27
+ }, totalWidth);
28
+
29
+ // Leave this for if we ever try to fix the auto width columns
30
+ // There is a bug where borders cause the offset to be wrong, this keeps it in sync.
31
+ // Add offset that increases by 1 every two columns after index 1
32
+ // if (position >= 2) {
33
+ // const additionalOffset = Math.floor((position - 2) / 2) + 1;
34
+ // totalWidth += additionalOffset;
35
+ // }
36
+
37
+ return totalWidth;
38
+ };
39
+
40
+ // Applies sticky styles to the column header
41
+ export const applyStickyStylesToTableHeader = async (position: number, left: number) => {
42
+ const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
43
+ if (header) {
44
+ (header as HTMLElement).style.position = 'sticky';
45
+ (header as HTMLElement).style.left = `${left}px`;
46
+ (header as HTMLElement).style.zIndex = '3';
47
+ (header as HTMLElement).style.backgroundColor =
48
+ 'var(--pf-table-pinned-column-background-color)';
49
+ }
50
+ };
51
+
52
+ // Sorts the pinned columns so that any column that is pinned comes before any column that is not.
53
+ export const sortPinnedColumns = <T>(columns: T[], pinnedColumnIds: string[]): T[] => {
54
+ return [...columns].sort((a, b) => {
55
+ const aIsPinned = pinnedColumnIds.includes((a as any).id);
56
+ const bIsPinned = pinnedColumnIds.includes((b as any).id);
57
+
58
+ if (aIsPinned && !bIsPinned) return -1; // a comes first
59
+ if (!aIsPinned && bIsPinned) return 1; // b comes first
60
+ return 0; // maintain relative order for columns with same pinned state
61
+ });
62
+ };
63
+
64
+ // Gets the styles for the pinned columns
65
+ export const getPinnedColumnStyles = (
66
+ isPinned: boolean,
67
+ index: number,
68
+ pinnedColumnIds: string[],
69
+ ): CSSObject => {
70
+ return isPinned
71
+ ? {
72
+ position: 'sticky',
73
+ left: `${getPreviousHeadersWidth(index, pinnedColumnIds)}px`,
74
+ zIndex: 3,
75
+ backgroundColor: 'var(--pf-table-pinned-column-background-color)',
76
+ }
77
+ : {
78
+ position: undefined,
79
+ left: undefined,
80
+ zIndex: undefined,
81
+ backgroundColor: undefined,
82
+ };
83
+ };
84
+
85
+ export const clearStickyStyles = (header: HTMLElement) => {
86
+ header.style.position = '';
87
+ header.style.left = '';
88
+ header.style.zIndex = '';
89
+ header.style.backgroundColor = '';
90
+ };
@@ -0,0 +1,146 @@
1
+ import { useEffect, useCallback, useMemo } from 'react';
2
+ import { TableColumn } from '../types';
3
+ import { sortPinnedColumns, getPreviousHeadersWidth } from '../components/helpers';
4
+ import { processColumns } from '../utils/processColumns';
5
+
6
+ /**
7
+ * Hook to manage pinned columns in a table
8
+ * Handles initialization, toggling, positioning and resizing of pinned columns
9
+ */
10
+ export const usePinnedColumnsManager = <T>(
11
+ columns: TableColumn<T>[],
12
+ canPinColumns: boolean,
13
+ onPinnedColumnsChange?: (pinnedColumnIds: string[]) => void,
14
+ ) => {
15
+ const pinnedColumnIds = columns.filter((column) => column.isPinned).map((column) => column.id);
16
+
17
+ // `dataColumnIds` is the list of IDs used as `data-column-id` attributes on the table headers and cells
18
+ const dataColumnIds = useMemo(() => {
19
+ const ids = columns
20
+ .map((column, index) => (column.isPinned ? `sticky-column-${index}` : null))
21
+ .filter((id): id is string => id !== null);
22
+
23
+ return ids.length > 0 ? ['checkbox-column', ...ids] : ids;
24
+ }, [columns]);
25
+
26
+ // Toggle individual column pin state
27
+ const togglePinnedColumn = useCallback(
28
+ (columnId: string) => {
29
+ const prevPinnedColumns = pinnedColumnIds;
30
+
31
+ // Handle unpinning
32
+ if (prevPinnedColumns.some((id) => id === columnId)) {
33
+ onPinnedColumnsChange?.(prevPinnedColumns.filter((id) => id !== columnId));
34
+ } else {
35
+ onPinnedColumnsChange?.(prevPinnedColumns.concat(columnId));
36
+ }
37
+ },
38
+ [pinnedColumnIds, onPinnedColumnsChange],
39
+ );
40
+
41
+ // Handle resize events and recalculate pinned column positions
42
+ useEffect(() => {
43
+ if (!canPinColumns) return;
44
+
45
+ const recalculatePositions = () => {
46
+ // Reset all column styles and remove last-pinned-column class
47
+ const allCells = document.querySelectorAll('.rdt_TableCol, .rdt_TableCell');
48
+ allCells.forEach((cell) => {
49
+ (cell as HTMLElement).style.position = '';
50
+ (cell as HTMLElement).style.left = '';
51
+ (cell as HTMLElement).style.zIndex = '';
52
+ (cell as HTMLElement).style.backgroundColor = '';
53
+ (cell as HTMLElement).style.borderRight = '';
54
+ });
55
+
56
+ // Apply styles to pinned columns
57
+ dataColumnIds.forEach((column, index) => {
58
+ const isLastPinnedColumn = index === dataColumnIds.length - 1;
59
+
60
+ if (column === 'checkbox-column') {
61
+ // Handle header checkbox
62
+ const headerCheckbox = document.querySelector('.rdt_TableCol:not([data-column-id])');
63
+ if (headerCheckbox) {
64
+ (headerCheckbox as HTMLElement).style.position = 'sticky';
65
+ (headerCheckbox as HTMLElement).style.left = '0';
66
+ (headerCheckbox as HTMLElement).style.zIndex = '4';
67
+ (headerCheckbox as HTMLElement).style.backgroundColor =
68
+ 'var(--pf-table-background-color)';
69
+ }
70
+
71
+ // Handle cell checkboxes
72
+ const cellCheckboxes = document.querySelectorAll('.rdt_TableCell:first-child');
73
+ cellCheckboxes.forEach((cell) => {
74
+ (cell as HTMLElement).style.position = 'sticky';
75
+ (cell as HTMLElement).style.left = '0';
76
+ (cell as HTMLElement).style.zIndex = '2';
77
+ (cell as HTMLElement).style.backgroundColor =
78
+ 'var(--pf-table-pinned-column-background-color)';
79
+ if (isLastPinnedColumn) {
80
+ (cell as HTMLElement).style.borderRight =
81
+ `2px solid var(--pf-table-pinned-column-border-color)`;
82
+ }
83
+ });
84
+ } else {
85
+ const columnIndex = parseInt(column.split('-')[2]);
86
+ const left = getPreviousHeadersWidth(columnIndex, dataColumnIds);
87
+
88
+ // Headers
89
+ const headers = document.querySelectorAll(
90
+ `.rdt_TableCol[data-column-id="sticky-column-${columnIndex}"]`,
91
+ );
92
+ headers.forEach((header) => {
93
+ (header as HTMLElement).style.position = 'sticky';
94
+ (header as HTMLElement).style.left = `${left}px`;
95
+ (header as HTMLElement).style.zIndex = '2';
96
+ (header as HTMLElement).style.backgroundColor =
97
+ 'var(--pf-table-pinned-column-background-color)';
98
+ if (isLastPinnedColumn) {
99
+ (header as HTMLElement).style.borderRight =
100
+ `2px solid var(--pf-table-pinned-column-border-color)`;
101
+ }
102
+ });
103
+
104
+ // Cells
105
+ const cells = document.querySelectorAll(
106
+ `.rdt_TableCell[data-column-id="sticky-column-${columnIndex}"]`,
107
+ );
108
+ cells.forEach((cell) => {
109
+ (cell as HTMLElement).style.position = 'sticky';
110
+ (cell as HTMLElement).style.left = `${left}px`;
111
+ (cell as HTMLElement).style.zIndex = '2';
112
+ (cell as HTMLElement).style.backgroundColor =
113
+ 'var(--pf-table-pinned-column-background-color)';
114
+ if (isLastPinnedColumn) {
115
+ (cell as HTMLElement).style.borderRight =
116
+ `2px solid var(--pf-table-pinned-column-border-color)`;
117
+ }
118
+ });
119
+ }
120
+ });
121
+ };
122
+
123
+ // Set up resize observers
124
+ const table = document.querySelector('.rdt_Table');
125
+ const resizeObserver = new ResizeObserver(recalculatePositions);
126
+
127
+ if (table) {
128
+ resizeObserver.observe(table);
129
+ }
130
+ window.addEventListener('resize', recalculatePositions);
131
+
132
+ return () => {
133
+ resizeObserver.disconnect();
134
+ window.removeEventListener('resize', recalculatePositions);
135
+ };
136
+ }, [canPinColumns, dataColumnIds]);
137
+
138
+ // Process columns for rendering with pin state
139
+ const columnsWithPinning = canPinColumns
140
+ ? sortPinnedColumns(processColumns(columns, dataColumnIds, togglePinnedColumn), dataColumnIds)
141
+ : columns;
142
+
143
+ return {
144
+ columnsWithPinning, // Columns with pin state and handlers applied
145
+ };
146
+ };