From 86d7d0716ad9dc7af6bfd56c98166b1cf931e571 Mon Sep 17 00:00:00 2001 From: Andrew Holloway Date: Tue, 5 Nov 2024 12:12:44 -0600 Subject: [PATCH] feat(DataTable): allow for pinning columns (#2084) - first column is pinned by default - allow for other columns to be pinned - clean up existing stories to fit with design --- src/components/DataTable/DataTable.module.css | 26 +- .../DataTable/DataTable.stories.tsx | 37 +- src/components/DataTable/DataTable.tsx | 85 +- .../__snapshots__/DataTable.test.ts.snap | 2131 +++++++++++++++-- 4 files changed, 2107 insertions(+), 172 deletions(-) diff --git a/src/components/DataTable/DataTable.module.css b/src/components/DataTable/DataTable.module.css index 42bf017a8..785fecbce 100644 --- a/src/components/DataTable/DataTable.module.css +++ b/src/components/DataTable/DataTable.module.css @@ -180,6 +180,9 @@ border-bottom: calc(var(--eds-border-width-sm) * 1px) solid; position: sticky; top: -1px; + + /* adding to z-index above any of the contents of tbody */ + z-index: 2; } .data-table__group-row { @@ -200,9 +203,30 @@ } } -.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 */ + + /* inherit the background color from the enclosing row */ + background-color: inherit; + position: sticky; + z-index: 1; + + &::after { + /* TODO: re-attach to a proper token value if possible */ + box-shadow: 1px 0px 0px rgba(0, 0, 0, 0.25), 2px 0 3px 0 rgba(0, 0, 0, 0.25); + display: block; + height: 100%; + content: ' '; + position: absolute; + top: 0; + right: 0; + width: 1px; + } } /** diff --git a/src/components/DataTable/DataTable.stories.tsx b/src/components/DataTable/DataTable.stories.tsx index 4c6d40767..448bcb2a2 100644 --- a/src/components/DataTable/DataTable.stories.tsx +++ b/src/components/DataTable/DataTable.stories.tsx @@ -161,11 +161,7 @@ const columnHelper = DataTableUtils.createColumnHelper(); const columns = [ columnHelper.accessor('firstName', { header: () => ( - + First Name ), @@ -792,7 +788,36 @@ export const StatusRows: StoryObj = { }, }; -// 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: { + tableStyle: 'border', + size: 'sm', + }, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const table = DataTableUtils.useReactTable({ + data: [...defaultData, ...defaultData], + columns, + getCoreRowModel: DataTableUtils.getCoreRowModel(), + initialState: { + columnPinning: { + left: ['firstName'], + }, + }, + }); + + return ; + }, +}; export const DefaultWithCustomTable: StoryObj = { args: { diff --git a/src/components/DataTable/DataTable.tsx b/src/components/DataTable/DataTable.tsx index 56eb216d2..eabf9e156 100644 --- a/src/components/DataTable/DataTable.tsx +++ b/src/components/DataTable/DataTable.tsx @@ -172,7 +172,6 @@ export function DataTable({ ...rest }: DataTableProps) { const componentClassName = clsx(styles['data-table'], className); - /** * to handle (sub-)caption, render outside the table, but put the combined text in * , and set to hidden in css. that way you can control the layout of the combined @@ -237,13 +236,23 @@ export function DataTable({ 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 ( {header.isPlaceholder @@ -270,17 +279,30 @@ export function DataTable({ /> {row.getLeafRows().map((row) => ( - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} + {row.getVisibleCells().map((cell) => { + const cellClassNames = clsx( + styles['data-table__cell-container'], + cell.column.getIsPinned() === 'left' && + styles['data-table--column-is-pinned'], + ); + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ); + })} ))} @@ -292,17 +314,30 @@ export function DataTable({ isStatusEligible ? row.getValue('status') : undefined } > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} + {row.getVisibleCells().map((cell) => { + const cellClassNames = clsx( + styles['data-table__cell-container'], + cell.column.getIsPinned() === 'left' && + styles['data-table--column-is-pinned'], + ); + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ); + })} )} @@ -503,7 +538,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, ); }, diff --git a/src/components/DataTable/__snapshots__/DataTable.test.ts.snap b/src/components/DataTable/__snapshots__/DataTable.test.ts.snap index a43e5152b..949a0f754 100644 --- a/src/components/DataTable/__snapshots__/DataTable.test.ts.snap +++ b/src/components/DataTable/__snapshots__/DataTable.test.ts.snap @@ -45,20 +45,6 @@ exports[` Default story renders snapshot 1`] = `
-
@@ -1088,59 +1074,1909 @@ exports[` Grouping story renders snapshot 1`] = ` colspan="1" >
+
+ Name +
+
+ + +
+
+ Country of Origin +
+
+ + +
+
+ Year +
+
+ + + + + + + Reds + + + + +
+
+ Merlot +
+
+ + +
+
+ France +
+
+ + +
+
+ 2016 +
+
+ + + + +
+
+ Pinot Noir +
+
+ + +
+
+ France +
+
+ + +
+
+ 2018 +
+
+ + + + +
+
+ Chianti +
+
+ + +
+
+ Italy +
+
+ + +
+
+ 2018 +
+
+ + + + + Whites + + + + +
+
+ Chardonnay +
+
+ + +
+
+ France +
+
+ + +
+
+ 2016 +
+
+ + + + +
+
+ Riesling +
+
+ + +
+
+ Germany +
+
+ + +
+
+ 2018 +
+
+ + + + + Rosés + + + + +
+
+ Rosado +
+
+ + +
+
+ Spain +
+
+ + +
+
+ 2021 +
+
+ + + + +
+
+ Rosato +
+
+ + +
+
+ Italy +
+
+ + +
+
+ 2024 +
+
+ + + + +
+`; + +exports[` HorizontalScrolling story renders snapshot 1`] = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - @@ -1161,7 +2997,7 @@ exports[` Grouping story renders snapshot 1`] = `
- France + Miller
@@ -1174,24 +3010,20 @@ exports[` Grouping story renders snapshot 1`] = `
- 2016 + 40
-
- @@ -1199,32 +3031,38 @@ exports[` Grouping story renders snapshot 1`] = ` class="data-table__cell-container" >
- France + 80 + + Complete +
+
+ - - @@ -1242,12 +3080,12 @@ exports[` Grouping story renders snapshot 1`] = ` class="data-table__cell-container" >
- Italy + 24
@@ -1260,24 +3098,35 @@ exports[` Grouping story renders snapshot 1`] = `
- 2018 + 100
-
- @@ -1298,7 +3147,7 @@ exports[` Grouping story renders snapshot 1`] = `
- France + Dirte
@@ -1311,7 +3160,38 @@ exports[` Grouping story renders snapshot 1`] = `
- 2016 + 45 +
+ + +
+ @@ -1320,7 +3200,8 @@ exports[` Grouping story renders snapshot 1`] = ` class="data-table__row" > @@ -1341,7 +3222,7 @@ exports[` Grouping story renders snapshot 1`] = `
- Germany + Miller
@@ -1354,37 +3235,48 @@ exports[` Grouping story renders snapshot 1`] = `
- 2018 + 40
-
- - - + + @@ -1400,29 +3292,25 @@ exports[` Grouping story renders snapshot 1`] = ` class="data-table__cell-container" >
- 2021 + Lindsey
-
- @@ -1430,12 +3318,12 @@ exports[` Grouping story renders snapshot 1`] = ` class="data-table__cell-container" >
- Italy + 100
@@ -1448,7 +3336,12 @@ exports[` Grouping story renders snapshot 1`] = `
- 2024 + 50 + + Incomplete +
@@ -1478,20 +3371,6 @@ exports[` RowStyleLined story renders snapshot 1`] = `
-
@@ -5101,20 +6980,6 @@ exports[` TableSizeSm story renders snapshot 1`] = `
-
@@ -6109,20 +7974,6 @@ exports[` TableStyleBorder story renders snapshot 1`] = `
-
+
+
+ First Name + + Given Name + +
+
+
+
+
+ Last Name + + Surname + +
+
+
+
+
+ Age +
+
+
+
+
+ Visits +
+
+
+
+
+ Profile Progress + + "Complete" is > 80% + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
+
+ 45 +
+
+
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete + +
+
+
+
+
+ Tandy +
+
+
+
+
+ Miller +
+
+
+
+
+ 40 +
+
+
+
+
+ 40 +
+
+
+
+
+ 80 + + Complete + +
+
+
+
+
+ Tanner +
+
+
+
+
+ Lindsey +
+
+
+
+
+ 24 +
+
+
+
+
+ 100 +
+
+
+
+
+ 50 + + Incomplete + +
+
+
+
+
+ Joe +
+
+
+
+
+ Dirte +
+
+
+
- Name + 45
- -
+
- Country of Origin + 20
- -
+
- Year + 10 + + Incomplete +
- -
- Reds
Grouping story renders snapshot 1`] = `
- Merlot + Tandy
- Pinot Noir + 40
- 2018 + Tanner
@@ -1234,7 +3072,7 @@ exports[` Grouping story renders snapshot 1`] = `
- Chianti + Lindsey
- Whites +
+
+ 50 + + Incomplete + +
+
Grouping story renders snapshot 1`] = `
- Chardonnay + Joe
+
+
+ 20 +
+
+
+
+
+ 10 + + Incomplete +
Grouping story renders snapshot 1`] = `
- Riesling + Tandy
- Rosés +
+
+ 40 +
+
- Rosado + 80 + + Complete +
Grouping story renders snapshot 1`] = `
- Spain + Tanner
- Rosato + 24