Skip to content

Commit

Permalink
feat(DataTable): allow for pinning columns
Browse files Browse the repository at this point in the history
- first column is pinned by default
- allow for other columns to be pinned
  • Loading branch information
booc0mtaco committed Oct 29, 2024
1 parent e28af44 commit 4e5e636
Show file tree
Hide file tree
Showing 4 changed files with 1,124 additions and 26 deletions.
13 changes: 12 additions & 1 deletion src/components/DataTable/DataTable.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,20 @@
}
}

.data-table--is-pinned {
.data-table--row-is-pinned {
box-shadow: var(--eds-box-shadow-sm);
}

.data-table--column-is-pinned {
/* When pinning columns, width and offset position are dynamic */
box-shadow: var(--eds-box-shadow-sm);

/* inherit the background color from the enclosing row */
background-color: inherit;

position: sticky;

z-index: 1;
}

/**
Expand Down
31 changes: 30 additions & 1 deletion src/components/DataTable/DataTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,36 @@ export const StatusRows: StoryObj<Args> = {
},
};

// TODO: Story for sticky column pinning (https://tanstack.com/table/latest/docs/framework/react/examples/column-pinning-sticky)
/**
* Allow for fixing some columns to not scroll off the screen, like freezing a column
*
* * This first column is sticky by default
* * Subsequent rows can be made sticky as well
*
* See: https://tanstack.com/table/latest/docs/framework/react/examples/column-pinning-sticky
* See: https://tanstack.com/table/latest/docs/guide/column-pinning
*/
export const HorizontalScrolling: StoryObj<Args> = {
args: {
tableStyle: 'border',
size: 'sm',
},
render: (args) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const table = DataTableUtils.useReactTable({
data: defaultData,
columns,
getCoreRowModel: DataTableUtils.getCoreRowModel(),
initialState: {
columnPinning: {
left: ['firstName'],
},
},
});

return <DataTable {...args} className="w-[800px]" table={table} />;
},
};

export const DefaultWithCustomTable: StoryObj<Args> = {
args: {
Expand Down
85 changes: 61 additions & 24 deletions src/components/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export function DataTable<T>({
...rest
}: DataTableProps<T>) {
const componentClassName = clsx(styles['data-table'], className);
// TODO-AH: need to add in the shadow and fix vert scroll overlap

/**
* to handle (sub-)caption, render outside the table, but put the combined text in
Expand Down Expand Up @@ -237,13 +238,23 @@ export function DataTable<T>({
header.getSize() !== 150
? `${header.getSize()}px`
: undefined;

const headerClassNames = clsx(
styles['data-table__header-cell-container'],
header.column.getIsPinned() === 'left' &&
styles['data-table--column-is-pinned'],
);
return (
<th
className={styles['data-table__header-cell-container']}
className={headerClassNames}
colSpan={header.colSpan}
key={header.id}
style={{
width: columnWidth,
left:
header.column.getIsPinned() === 'left'
? `${header.column.getStart('left')}px`
: undefined,
}}
>
{header.isPlaceholder
Expand All @@ -270,17 +281,30 @@ export function DataTable<T>({
/>
{row.getLeafRows().map((row) => (
<DataTableRow key={row.id}>
{row.getVisibleCells().map((cell) => (
<td
className={styles['data-table__cell-container']}
key={cell.id}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
))}
{row.getVisibleCells().map((cell) => {
const cellClassNames = clsx(
styles['data-table__cell-container'],
cell.column.getIsPinned() === 'left' &&
styles['data-table--column-is-pinned'],
);
return (
<td
className={cellClassNames}
key={cell.id}
style={{
left:
cell.column.getIsPinned() === 'left'
? `${cell.column.getStart('left')}px`
: undefined,
}}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
);
})}
</DataTableRow>
))}
</>
Expand All @@ -292,17 +316,30 @@ export function DataTable<T>({
isStatusEligible ? row.getValue('status') : undefined
}
>
{row.getVisibleCells().map((cell) => (
<td
className={styles['data-table__cell-container']}
key={cell.id}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
))}
{row.getVisibleCells().map((cell) => {
const cellClassNames = clsx(
styles['data-table__cell-container'],
cell.column.getIsPinned() === 'left' &&
styles['data-table--column-is-pinned'],
);
return (
<td
className={cellClassNames}
key={cell.id}
style={{
left:
cell.column.getIsPinned() === 'left'
? `${cell.column.getStart('left')}px`
: undefined,
}}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
);
})}
</DataTableRow>
)}
</React.Fragment>
Expand Down Expand Up @@ -503,7 +540,7 @@ export const DataTableTable = ({
observer = new IntersectionObserver(
([event]) => {
return event.target.classList.toggle(
styles['data-table--is-pinned'],
styles['data-table--row-is-pinned'],
event.intersectionRatio < 1,
);
},
Expand Down
Loading

0 comments on commit 4e5e636

Please sign in to comment.