@indico-data/design-system 2.50.0 → 2.51.1

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 (74) hide show
  1. package/lib/components/tanstackTable/TankstackTable.types.d.ts +45 -0
  2. package/lib/components/tanstackTable/TanstackTable.stories.d.ts +16 -0
  3. package/lib/components/tanstackTable/TanstakTable.d.ts +4 -0
  4. package/lib/components/tanstackTable/__tests__/TanstackTable.test.d.ts +1 -0
  5. package/lib/components/tanstackTable/__tests__/__mocks__/test-mock-data.d.ts +8 -0
  6. package/lib/components/tanstackTable/components/ActionBar/ActionBar.d.ts +17 -0
  7. package/lib/components/tanstackTable/components/ActionBar/ActionBar.stories.d.ts +10 -0
  8. package/lib/components/tanstackTable/components/ActionBar/__tests__/ActionBar.test.d.ts +1 -0
  9. package/lib/components/tanstackTable/components/ActionBar/index.d.ts +1 -0
  10. package/lib/components/tanstackTable/components/NoResults/NoResults.d.ts +7 -0
  11. package/lib/components/tanstackTable/components/NoResults/__tests__/NoResult.test.d.ts +1 -0
  12. package/lib/components/tanstackTable/components/NoResults/index.d.ts +1 -0
  13. package/lib/components/tanstackTable/components/TableBody/TableBody.d.ts +11 -0
  14. package/lib/components/tanstackTable/components/TableBody/index.d.ts +1 -0
  15. package/lib/components/tanstackTable/components/TableHeader/TableHeader.d.ts +6 -0
  16. package/lib/components/tanstackTable/components/TableHeader/index.d.ts +1 -0
  17. package/lib/components/tanstackTable/components/TablePagination/TablePagination.d.ts +9 -0
  18. package/lib/components/tanstackTable/components/TablePagination/__tests__/TablePagination.test.d.ts +1 -0
  19. package/lib/components/tanstackTable/components/TablePagination/index.d.ts +1 -0
  20. package/lib/components/tanstackTable/docs/pinnedColumns/PinnedColumn.stories.d.ts +7 -0
  21. package/lib/components/tanstackTable/docs/withRowClick/WithRowClick.stories.d.ts +7 -0
  22. package/lib/components/tanstackTable/helpers.d.ts +830 -0
  23. package/lib/components/tanstackTable/index.d.ts +2 -0
  24. package/lib/components/tanstackTable/mock-data/mock-data.d.ts +14 -0
  25. package/lib/components/tanstackTable/mock-data/table-configuration.d.ts +3 -0
  26. package/lib/components/tanstackTable/useTanstackTable.d.ts +14 -0
  27. package/lib/index.css +262 -0
  28. package/lib/index.d.ts +67 -17
  29. package/lib/index.esm.css +262 -0
  30. package/lib/index.esm.js +20432 -56
  31. package/lib/index.esm.js.map +1 -1
  32. package/lib/index.js +20432 -55
  33. package/lib/index.js.map +1 -1
  34. package/package.json +2 -1
  35. package/src/components/tanstackTable/TankstackTable.types.ts +44 -0
  36. package/src/components/tanstackTable/TanstackTable.mdx +122 -0
  37. package/src/components/tanstackTable/TanstackTable.stories.tsx +284 -0
  38. package/src/components/tanstackTable/TanstakTable.tsx +156 -0
  39. package/src/components/tanstackTable/__tests__/TanstackTable.test.tsx +73 -0
  40. package/src/components/tanstackTable/__tests__/__mocks__/test-mock-data.tsx +83 -0
  41. package/src/components/tanstackTable/components/ActionBar/ActionBar.mdx +10 -0
  42. package/src/components/tanstackTable/components/ActionBar/ActionBar.scss +30 -0
  43. package/src/components/tanstackTable/components/ActionBar/ActionBar.stories.tsx +98 -0
  44. package/src/components/tanstackTable/components/ActionBar/ActionBar.tsx +43 -0
  45. package/src/components/tanstackTable/components/ActionBar/__tests__/ActionBar.test.tsx +65 -0
  46. package/src/components/tanstackTable/components/ActionBar/index.ts +1 -0
  47. package/src/components/tanstackTable/components/NoResults/NoResults.scss +24 -0
  48. package/src/components/tanstackTable/components/NoResults/NoResults.tsx +22 -0
  49. package/src/components/tanstackTable/components/NoResults/__tests__/NoResult.test.tsx +25 -0
  50. package/src/components/tanstackTable/components/NoResults/index.ts +1 -0
  51. package/src/components/tanstackTable/components/TableBody/TableBody.tsx +77 -0
  52. package/src/components/tanstackTable/components/TableBody/index.ts +1 -0
  53. package/src/components/tanstackTable/components/TableHeader/TableHeader.tsx +49 -0
  54. package/src/components/tanstackTable/components/TableHeader/index.ts +1 -0
  55. package/src/components/tanstackTable/components/TablePagination/TablePagination.tsx +45 -0
  56. package/src/components/tanstackTable/components/TablePagination/__tests__/TablePagination.test.tsx +18 -0
  57. package/src/components/tanstackTable/components/TablePagination/index.ts +1 -0
  58. package/src/components/tanstackTable/docs/pinnedColumns/PinnedColumn.mdx +34 -0
  59. package/src/components/tanstackTable/docs/pinnedColumns/PinnedColumn.stories.tsx +40 -0
  60. package/src/components/tanstackTable/docs/withRowClick/WithRowClick.mdx +48 -0
  61. package/src/components/tanstackTable/docs/withRowClick/WithRowClick.stories.tsx +32 -0
  62. package/src/components/tanstackTable/helpers.ts +45 -0
  63. package/src/components/tanstackTable/index.ts +2 -0
  64. package/src/components/tanstackTable/mock-data/mock-data.ts +256 -0
  65. package/src/components/tanstackTable/mock-data/table-configuration.tsx +222 -0
  66. package/src/components/tanstackTable/styles/_variables.scss +35 -0
  67. package/src/components/tanstackTable/styles/table.scss +218 -0
  68. package/src/components/tanstackTable/styles/test.scss +19 -0
  69. package/src/components/tanstackTable/tanstack-table.d.ts +19 -0
  70. package/src/components/tanstackTable/useTanstackTable.tsx +39 -0
  71. package/src/index.ts +1 -0
  72. package/src/legacy/components/loading-indicators/CirclePulse/CirclePulse.tsx +1 -0
  73. package/src/styles/index.scss +1 -0
  74. package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.tsx +16 -2
@@ -0,0 +1,83 @@
1
+ import { Checkbox } from "@/components/forms/checkbox";
2
+ import { ColumnDef } from "@tanstack/react-table";
3
+
4
+ export interface TestData {
5
+ id: string;
6
+ name: string;
7
+ age: number;
8
+ }
9
+
10
+ export const columns: ColumnDef<TestData>[] = [
11
+ {
12
+ id: 'select',
13
+ size: 48,
14
+ meta: {
15
+ styles: {
16
+ definedColumnSize: true,
17
+ header: {
18
+ hasNoPadding: true,
19
+ },
20
+ cell: {
21
+ hasNoPadding: true,
22
+ },
23
+ },
24
+ },
25
+ header: ({ table }) => (
26
+ <div className="p-3">
27
+ <Checkbox
28
+ name="select-all"
29
+ id="select-all"
30
+ label=""
31
+ isChecked={table.getIsAllPageRowsSelected()}
32
+ onChange={() => {
33
+ table
34
+ .getRowModel()
35
+ .rows.forEach((row) =>
36
+ row.toggleSelected(!table.getRowModel().rows[0].getIsSelected()),
37
+ );
38
+ }}
39
+ isDisabled={false}
40
+ aria-label="select-rows-header"
41
+ />
42
+ </div>
43
+ ),
44
+ cell: ({ row }) => (
45
+ <div className="p-3">
46
+ <Checkbox
47
+ name="select"
48
+ id={row.id}
49
+ label=""
50
+ data-testid={`checkbox-${row.id}`}
51
+ aria-label={`checkbox-${row.id}`}
52
+ isChecked={row.getIsSelected()}
53
+ isDisabled={!row.getCanSelect()}
54
+ onChange={row.getToggleSelectedHandler()}
55
+ />
56
+ </div>
57
+ ),
58
+ },
59
+ {
60
+ id: 'firstName',
61
+ accessorKey: 'firstName',
62
+ header: () => 'First Name',
63
+ cell: (info) => info.getValue(),
64
+ footer: (props) => props.column.id,
65
+ },
66
+ {
67
+ id: 'name',
68
+ accessorKey: 'name',
69
+ header: 'Name',
70
+ cell: ({ getValue }) => getValue(),
71
+ },
72
+ {
73
+ id: 'age',
74
+ accessorKey: 'age',
75
+ header: 'Age',
76
+ cell: ({ getValue }) => getValue(),
77
+ },
78
+ ];
79
+
80
+ export const data: TestData[] = [
81
+ { id: '1', name: 'Alice', age: 30 },
82
+ { id: '2', name: 'Bob', age: 25 },
83
+ ];
@@ -0,0 +1,10 @@
1
+ import { Canvas, Meta, Controls } from '@storybook/blocks';
2
+ import * as ActionBarStories from './ActionBar.stories';
3
+
4
+ <Meta title="Layout/Tanstack Table/Action Bar" name="ActionBar" />
5
+
6
+ # Table
7
+
8
+ <Canvas of={ActionBarStories.Default} />
9
+
10
+ <Controls of={ActionBarStories.Default} />
@@ -0,0 +1,30 @@
1
+ .tanstack-table__action-bar {
2
+ position: fixed;
3
+ bottom: 170px;
4
+ left: 50%;
5
+ transform: translateX(-50%);
6
+ z-index: 90;
7
+ box-shadow: 3px 1px 15px 0 rgba(0, 0, 0, 0.85);
8
+ &__container {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: var(--pf-size-2);
12
+ }
13
+
14
+ &__text-container {
15
+ padding: var(--pf-padding-2);
16
+
17
+ span {
18
+ font-size: var(--pf-font-size-base);
19
+ font-weight: var(--pf-font-weight-medium);
20
+ line-height: var(--pf-line-height-lg);
21
+ color: var(--pf-white-color);
22
+ }
23
+ }
24
+
25
+ &__button-container {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: var(--pf-size-4);
29
+ }
30
+ }
@@ -0,0 +1,98 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { ActionBar } from './ActionBar';
3
+ import { Table } from '@tanstack/react-table';
4
+ import { Button } from '@/components/button';
5
+
6
+ type DummyRow = {
7
+ id: string;
8
+ isSelected: boolean;
9
+ getIsSelected: () => boolean;
10
+ toggleSelected: (value: boolean) => void;
11
+ };
12
+
13
+ const createDummyRow = (id: string, isSelected = false): DummyRow => ({
14
+ id,
15
+ isSelected,
16
+ getIsSelected() {
17
+ return this.isSelected;
18
+ },
19
+ toggleSelected(value: boolean) {
20
+ this.isSelected = value;
21
+ },
22
+ });
23
+
24
+ const dummyTable = {
25
+ getRowModel: () => ({
26
+ rows: [createDummyRow('1', true), createDummyRow('2', false), createDummyRow('3', true)],
27
+ }),
28
+ } as unknown as Table<{ id: string }>;
29
+
30
+ const meta: Meta = {
31
+ title: 'Layout/Tanstack Table/Action Bar',
32
+ component: ActionBar,
33
+ args: {
34
+ className: 'tanstack-table__action-bar-docs',
35
+ table: dummyTable,
36
+ },
37
+ argTypes: {
38
+ table: {
39
+ description: 'Table instance.',
40
+ control: false,
41
+ table: {
42
+ category: 'Props',
43
+ type: { summary: 'Table<any>' },
44
+ },
45
+ },
46
+ className: {
47
+ description: 'class name for styling.',
48
+ control: false,
49
+ table: {
50
+ category: 'Props',
51
+ type: { summary: 'string' },
52
+ },
53
+ },
54
+ children: {
55
+ description: 'React node to display in the action bar.',
56
+ control: false,
57
+ table: {
58
+ category: 'Props',
59
+ type: { summary: 'React.ReactNode' },
60
+ },
61
+ },
62
+ TableActions: {
63
+ description: 'Component for table actions with selected items.',
64
+ control: false,
65
+ table: {
66
+ category: 'Props',
67
+ type: {
68
+ summary: 'React.ComponentType<{ selectedItems: Row<any>[]; unselectRows: () => void }>',
69
+ },
70
+ },
71
+ },
72
+ },
73
+ decorators: [
74
+ (Story) => (
75
+ <div style={{ width: '100%', height: '500px', overflow: 'hidden' }}>
76
+ <Story />
77
+ </div>
78
+ ),
79
+ ],
80
+ };
81
+
82
+ export default meta;
83
+
84
+ type Story = StoryObj<typeof ActionBar<{ id: string }>>;
85
+
86
+ export const Default: Story = {};
87
+
88
+ export const WithCustomTableActions: Story = {
89
+ args: {
90
+ TableActions: () => <Button ariaLabel="Clear">Clear</Button>,
91
+ },
92
+ };
93
+
94
+ export const WithChildren: Story = {
95
+ args: {
96
+ children: <p>test</p>,
97
+ },
98
+ };
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { Row, Table } from '@tanstack/react-table';
3
+ import { Card } from '@/components/card';
4
+ import classNames from 'classnames';
5
+
6
+ type Props<T extends object> = {
7
+ table: Table<T & { id: string }>;
8
+ TableActions?: React.ComponentType<{
9
+ selectedItems: Row<T & { id: string }>[];
10
+ unselectRows: () => void;
11
+ }>;
12
+ className?: string;
13
+ children?: React.ReactNode;
14
+ };
15
+
16
+ export function ActionBar<T extends object>({
17
+ table,
18
+ TableActions,
19
+ className,
20
+ children,
21
+ }: Props<T>) {
22
+ const selectedItems = table.getRowModel().rows.filter((row) => row.getIsSelected());
23
+
24
+ const unselectRows = () => {
25
+ table.getRowModel().rows.forEach((row) => {
26
+ row.toggleSelected(false);
27
+ });
28
+ };
29
+
30
+ return (
31
+ <Card className={classNames('tanstack-table__action-bar', className)}>
32
+ {children ?? (
33
+ <div className="tanstack-table__action-bar__container">
34
+ {TableActions && (
35
+ <div className="tanstack-table__action-bar__button-container">
36
+ <TableActions selectedItems={selectedItems} unselectRows={unselectRows} />
37
+ </div>
38
+ )}
39
+ </div>
40
+ )}
41
+ </Card>
42
+ );
43
+ }
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { ActionBar } from '../ActionBar';
4
+ import type { Table } from '@tanstack/react-table';
5
+
6
+ type DummyRow = {
7
+ getIsSelected: () => boolean;
8
+ toggleSelected: (value: boolean) => void;
9
+ };
10
+
11
+ const createDummyTable = (rows: DummyRow[]): Table<any> =>
12
+ ({
13
+ getRowModel: () => ({ rows }),
14
+ }) as Table<any>;
15
+
16
+ describe('ActionBar', () => {
17
+ it('renders correct when use a children prop', () => {
18
+ const dummyRows: DummyRow[] = [
19
+ { getIsSelected: () => true, toggleSelected: jest.fn() },
20
+ { getIsSelected: () => true, toggleSelected: jest.fn() },
21
+ ];
22
+ const dummyTable = createDummyTable(dummyRows);
23
+
24
+ render(
25
+ <ActionBar table={dummyTable}>
26
+ <div>
27
+ <p>Hello</p>
28
+ </div>
29
+ </ActionBar>,
30
+ );
31
+ expect(screen.getByText('Hello')).toBeDefined();
32
+ });
33
+
34
+ it('renders TableActions component when provided and calls unselectRows', () => {
35
+ const toggleRow1 = jest.fn();
36
+ const toggleRow2 = jest.fn();
37
+ const dummyRows: DummyRow[] = [
38
+ { getIsSelected: () => true, toggleSelected: toggleRow1 },
39
+ { getIsSelected: () => true, toggleSelected: toggleRow2 },
40
+ ];
41
+ const dummyTable = createDummyTable(dummyRows);
42
+
43
+ // Dummy TableActions component which calls unselectRows on button click
44
+ const DummyTableActions: React.FC<{
45
+ selectedItems: DummyRow[];
46
+ unselectRows: () => void;
47
+ }> = ({ selectedItems, unselectRows }) => (
48
+ <button onClick={unselectRows} data-testid="dummy-actions">
49
+ Clear Selection ({selectedItems.length})
50
+ </button>
51
+ );
52
+
53
+ render(<ActionBar table={dummyTable} TableActions={DummyTableActions as any} />);
54
+ const button = screen.getByTestId('dummy-actions');
55
+ expect(button).toBeDefined();
56
+ expect(button.textContent).toContain('2');
57
+
58
+ // Simulate click on the TableActions button
59
+ fireEvent.click(button);
60
+
61
+ // Verify unselectRows toggles all selected rows to false
62
+ expect(toggleRow1).toHaveBeenCalledWith(false);
63
+ expect(toggleRow2).toHaveBeenCalledWith(false);
64
+ });
65
+ });
@@ -0,0 +1 @@
1
+ export { ActionBar } from './ActionBar';
@@ -0,0 +1,24 @@
1
+ .tanstack-table__no-results {
2
+ display: flex;
3
+ justify-content: center;
4
+ flex-direction: column;
5
+ align-items: center;
6
+
7
+ position: sticky;
8
+ left: 0;
9
+ margin: var(--pf-margin-8) auto;
10
+
11
+ &__text {
12
+ font-size: var(--pf-line-height-md);
13
+ font-weight: var(--pf-font-weight-medium);
14
+ color: var(--pf-primary-color-50);
15
+
16
+ span {
17
+ color: var(--pf-white-color);
18
+ cursor: pointer;
19
+ &:hover {
20
+ text-decoration: underline;
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ import { Button } from '@/components/button/Button';
2
+
3
+ type Props = {
4
+ clearFilters?: () => void;
5
+ hasFilters?: boolean;
6
+ message: string;
7
+ };
8
+
9
+ export function NoResults({ clearFilters, hasFilters, message }: Props) {
10
+ return (
11
+ <div className="tanstack-table__no-results" data-testid="tanstack-table-no-results">
12
+ <p className="tanstack-table__no-results__text">{message}</p>
13
+ {hasFilters && (
14
+ <p className="tanstack-table__no-results__text">
15
+ <Button ariaLabel="Reset filters" variant="link" onClick={clearFilters}>
16
+ Reset filters
17
+ </Button>
18
+ </p>
19
+ )}
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,25 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { NoResults } from '../NoResults';
3
+
4
+ describe('NoResults Component', () => {
5
+ it('renders the message without filters applied', () => {
6
+ render(<NoResults message="No results found." />);
7
+ expect(screen.getByText('No results found.')).toBeInTheDocument();
8
+ expect(screen.queryByText('Reset filters')).not.toBeInTheDocument();
9
+ });
10
+
11
+ it('renders the clear filters option when filters are applied', () => {
12
+ render(<NoResults message="No results found." hasFilters />);
13
+ expect(screen.getByText('No results found.')).toBeInTheDocument();
14
+ const clearFiltersElement = screen.getByText('Reset filters');
15
+ expect(clearFiltersElement).toBeInTheDocument();
16
+ });
17
+
18
+ it('calls clearFilters when "Click here" is clicked', () => {
19
+ const mockClearFilters = jest.fn();
20
+ render(<NoResults message="No results found." hasFilters clearFilters={mockClearFilters} />);
21
+ const clearFiltersElement = screen.getByText('Reset filters');
22
+ fireEvent.click(clearFiltersElement);
23
+ expect(mockClearFilters).toHaveBeenCalled();
24
+ });
25
+ });
@@ -0,0 +1 @@
1
+ export { NoResults } from './NoResults';
@@ -0,0 +1,77 @@
1
+ import { flexRender, Table } from '@tanstack/react-table';
2
+ import { Row } from '@tanstack/react-table';
3
+ import classNames from 'classnames';
4
+ import { getTdStyles } from '../../helpers';
5
+ import { CirclePulse } from '@/legacy/components/loading-indicators/CirclePulse';
6
+ import { Dispatch, SetStateAction } from 'react';
7
+
8
+ export type Props<T> = {
9
+ table: Table<T>;
10
+ onRowClick?: ((row: T & { id: string }) => void) | null | undefined;
11
+ isLoading: boolean;
12
+ columnsLength: number;
13
+ activeRows: string[];
14
+ };
15
+
16
+ export const TableBody = <T,>({
17
+ table,
18
+ onRowClick,
19
+ isLoading,
20
+ columnsLength,
21
+ activeRows,
22
+ }: Props<T>) => {
23
+ return (
24
+ <>
25
+ {table.getRowModel().rows.map((row) => (
26
+ <tr
27
+ className={classNames('tanstack-table__tbody__tr', {
28
+ 'is-selected': row.getIsSelected(),
29
+ 'show-hover': !!onRowClick,
30
+ 'is-clicked': activeRows.includes(row.id), // Checkbox Is clicked
31
+ })}
32
+ key={row.id}
33
+ >
34
+ {row.getVisibleCells().map((cell) => {
35
+ const { columnDef } = cell.column;
36
+ return (
37
+ <td
38
+ className={classNames('tanstack-table__tbody__td', {
39
+ 'pa-0': !!columnDef.meta?.styles?.cell?.hasNoPadding,
40
+ })}
41
+ key={cell.id}
42
+ style={{
43
+ ...getTdStyles(
44
+ cell.column,
45
+ columnDef.meta?.styles?.cell?.textAlign,
46
+ columnDef.meta?.styles?.definedColumnSize,
47
+ cell.column.getSize(),
48
+ ),
49
+ }}
50
+ onClick={() => {
51
+ // if the cell is not preventRowSelection, then we can click the row
52
+ if (!columnDef.meta?.styles?.cell?.preventRowSelection) {
53
+ onRowClick?.(row as any);
54
+ }
55
+ }}
56
+ >
57
+ {flexRender(columnDef.cell, cell.getContext())}
58
+ </td>
59
+ );
60
+ })}
61
+ </tr>
62
+ ))}
63
+ {isLoading && (
64
+ <tr className="tanstack-table__tbody__tr">
65
+ <td
66
+ className={classNames('tanstack-table__centered-row', {
67
+ 'is-Loading': isLoading,
68
+ })}
69
+ colSpan={columnsLength}
70
+ >
71
+ <CirclePulse data-testid="loading-indicator" />
72
+ </td>
73
+ </tr>
74
+ )}
75
+ </>
76
+ );
77
+ };
@@ -0,0 +1 @@
1
+ export { TableBody } from './TableBody';
@@ -0,0 +1,49 @@
1
+ import { forwardRef } from 'react';
2
+ import { Column, flexRender, Header, Table } from '@tanstack/react-table';
3
+ import classNames from 'classnames';
4
+ import { getThStyles } from '../../helpers';
5
+
6
+ interface Props {
7
+ table: Table<any>;
8
+ }
9
+
10
+ export const TableHeader = forwardRef(({ table }: Props, ref: any) => {
11
+ const getStyles = (column: Column<any>, header: Header<any, any>) => {
12
+ return {
13
+ ...getThStyles(column),
14
+ ...(header.column.columnDef.meta?.styles?.header?.textAlign
15
+ ? { textAlign: header.column.columnDef.meta?.styles?.header?.textAlign }
16
+ : {}),
17
+ };
18
+ };
19
+
20
+ return (
21
+ <>
22
+ {table.getHeaderGroups().map((headerGroup) => (
23
+ <tr key={headerGroup.id}>
24
+ {headerGroup.headers.map((header) => {
25
+ const { column } = header;
26
+
27
+ return (
28
+ <th
29
+ className={classNames('tanstack-table__thead__th', {
30
+ 'pa-0': !!header.column.columnDef.meta?.styles?.header?.hasNoPadding,
31
+ })}
32
+ key={header.id}
33
+ ref={(el: any) => {
34
+ if (el) ref.current[column.id] = el;
35
+ }}
36
+ colSpan={header.colSpan}
37
+ style={{
38
+ ...getStyles(column, header),
39
+ }}
40
+ >
41
+ {flexRender(header.column.columnDef.header, header.getContext())}
42
+ </th>
43
+ );
44
+ })}
45
+ </tr>
46
+ ))}
47
+ </>
48
+ );
49
+ });
@@ -0,0 +1 @@
1
+ export { TableHeader } from './TableHeader';
@@ -0,0 +1,45 @@
1
+ import { Pagination as PaginationComponent } from '../../../pagination';
2
+ import { Row, Col } from '../../../grid';
3
+
4
+ interface TablePaginationProps {
5
+ rowsPerPage: number;
6
+ rowCount: number;
7
+ onChangePage: (page: number) => void;
8
+ currentPage: number;
9
+ totalEntriesText?: string;
10
+ }
11
+
12
+ export const TablePagination = ({
13
+ rowsPerPage,
14
+ rowCount,
15
+ onChangePage,
16
+ currentPage,
17
+ totalEntriesText,
18
+ }: TablePaginationProps) => {
19
+ const totalPages = Math.ceil(rowCount / rowsPerPage);
20
+
21
+ return (
22
+ <div className="table__pagination" data-testid="tanstack-table-pagination">
23
+ <Row align="center" justify="between">
24
+ <Col xs="content">
25
+ {totalEntriesText && (
26
+ <span
27
+ data-testid="table-pagination-total-entries"
28
+ className="table__pagination-total-entries"
29
+ >
30
+ {totalEntriesText}
31
+ </span>
32
+ )}
33
+ </Col>
34
+ <Col xs="content">
35
+ <PaginationComponent
36
+ data-testid="table-pagination-component"
37
+ totalPages={totalPages}
38
+ currentPage={currentPage}
39
+ onChange={(page) => onChangePage(page)}
40
+ />
41
+ </Col>
42
+ </Row>
43
+ </div>
44
+ );
45
+ };
@@ -0,0 +1,18 @@
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
+ expect(screen.getByTestId('tanstack-table-pagination')).toBeInTheDocument();
17
+ });
18
+ });
@@ -0,0 +1 @@
1
+ export * from './TablePagination';
@@ -0,0 +1,34 @@
1
+ import { Canvas, Meta, Controls } from '@storybook/blocks';
2
+ import * as TableStories from './PinnedColumn.stories';
3
+
4
+ <Meta title="Layout/Tanstack Table/Pinned Columns" name="Tanstack Table Pinned Column" />
5
+
6
+ # Tanstack Table Pinned Columns
7
+
8
+ <Canvas of={TableStories.PinnedColumns} />
9
+
10
+ This component enables the rapid creation of responsive, paginated tables using the React TanStack Table library.
11
+ It supports configuring pinned columns by extracting the necessary properties from your column creation object.
12
+ For more details on all configuration options and features, please refer to the official [TanStack Table Pinning Documentation](https://tanstack.com/table/latest/docs/guide/column-pinning#pin-columns-by-default).
13
+
14
+ To enable pinned columns, pass the following properties to your table.
15
+ In your column definition, extract the pinning information from the header object and pass it to your component in this case PinHeaderColumns as shown:
16
+
17
+ ```tsx
18
+ header: ({ header }) => (
19
+ <PinHeaderColumns
20
+ content="First Name"
21
+ onPinClick={() => header.column.pin(header.column.getIsPinned() ? false : 'left')}
22
+ isPinned={header.column.getIsPinned() === 'left' ? true : false}
23
+ />
24
+ ),
25
+
26
+ ```
27
+
28
+ This code toggles the pinned state of the column: if the column is already pinned, clicking will unpin it; otherwise, it will pin the column to the left
29
+
30
+ Additionally, the table component accepts a defaultPinnedColumns prop, which is an array of column IDs that will be pinned by default when the table is rendered. This ensures that important columns—such as selection checkboxes or key data fields—remain visible as users scroll through the table.
31
+
32
+ ```tsx
33
+ <TanstackTable defaultPinnedColumns={['select', 'firstName', 'lastName']} />
34
+ ```