From ec2d4d1ba8e570fb9e5c99368c591c2519e02f36 Mon Sep 17 00:00:00 2001 From: Matt Gallo Date: Thu, 6 Feb 2025 11:02:41 -0500 Subject: [PATCH 1/3] feat: add batch actions and row selection to filter panel --- react/filterPanel/src/Example/FilterPanel.tsx | 126 +++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/react/filterPanel/src/Example/FilterPanel.tsx b/react/filterPanel/src/Example/FilterPanel.tsx index 9fd4829..4ff607a 100644 --- a/react/filterPanel/src/Example/FilterPanel.tsx +++ b/react/filterPanel/src/Example/FilterPanel.tsx @@ -21,8 +21,18 @@ import { Popover, OperationalTag, PopoverContent, + Pagination, + TableBatchActions, + TableBatchAction, } from '@carbon/react'; -import { Close, Filter } from '@carbon/react/icons'; +import { + Add, + Close, + Download, + Filter, + Save, + TrashCan, +} from '@carbon/react/icons'; const { Table, TableBody, @@ -48,6 +58,8 @@ import { Header, getFacetedUniqueValues, ColumnFilter, + getPaginationRowModel, + PaginationState, } from '@tanstack/react-table'; import { rankItem } from '@tanstack/match-sorter-utils'; @@ -84,6 +96,49 @@ const tableId = 'table-' + Math.random().toString(36).substring(2, 15); const columnHelper = createColumnHelper(); const columns = [ + { + id: 'select', + width: 48, + header: ({ table }) => ( + // TableSelectAll throws DOM nesting error, using Checkbox instead to avoid this + { + const isIndeterminate = table.getIsSomeRowsSelected(); + if (!isIndeterminate) { + table.toggleAllPageRowsSelected(true); + } + if (table.getIsAllPageRowsSelected()) { + table.toggleAllRowsSelected(false); + return; + } + if (isIndeterminate) { + table.toggleAllPageRowsSelected(true); + return; + } + }, + id: 'batch-checkbox', + labelText: 'header checkbox', + hideLabel: true, + }} + /> + ), + cell: ({ row }) => ( + + ), + }, columnHelper.accessor((row) => row.name, { id: 'name', cell: (info) => {info.getValue()}, @@ -121,6 +176,11 @@ export const FilterPanel = () => { const [popoverOpen, setPopoverOpen] = useState(false); const [columnFilters, setColumnFilters] = useState([]); const [localFilters, setLocalFilters] = useState([]); + const [rowSelection, setRowSelection] = React.useState({}); + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 10, + }); const filterSummaryRef = useRef(); const measureTagRef = useRef(); @@ -143,13 +203,20 @@ export const FilterPanel = () => { state: { globalFilter, columnFilters, + rowSelection, + pagination, }, onColumnFiltersChange: setColumnFilters, getCoreRowModel: getCoreRowModel(), - getFilteredRowModel: getFilteredRowModel(), //client side filtering + getFilteredRowModel: getFilteredRowModel(), onGlobalFilterChange: setGlobalFilter, globalFilterFn: 'fuzzy', //apply fuzzy filter to the global filter (most common use case for fuzzy filter) getFacetedUniqueValues: getFacetedUniqueValues(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + enableRowSelection: (row) => row.original.status !== 'disabled', // conditionally disable rows + // enableRowSelection: row => row.original.age > 18, // or enable row selection + onRowSelectionChange: setRowSelection, }); interface ExtendedColFilter extends ColumnFilter { @@ -318,7 +385,7 @@ export const FilterPanel = () => { )); }; - console.log(table.getFilteredRowModel().rows.length === 0); + const shouldShowBatchActions = Object.keys(rowSelection).length > 0; return (
@@ -335,6 +402,43 @@ export const FilterPanel = () => { width: table.getCenterTotalSize(), }}> + table.resetRowSelection()} + onSelectAll={() => { + table.toggleAllRowsSelected(true); + }} + totalCount={data?.length}> + table.resetRowSelection()}> + Delete + + table.resetRowSelection()}> + Delete + + table.resetRowSelection()}> + Save + + table.resetRowSelection()}> + Download + + { ))} + { + table.setPageSize(Number(pageSize)); + table.setPageIndex(page - 1); + }} + />
); From 9175a8def9f36a276532ca2c1c6aa9da0ed8ff20 Mon Sep 17 00:00:00 2001 From: Matt Gallo Date: Thu, 6 Feb 2025 11:16:22 -0500 Subject: [PATCH 2/3] fix: update pagination layout with filter panel --- react/filterPanel/src/Example/FilterPanel.tsx | 20 +++++++++++++++++++ react/filterPanel/src/Example/example.scss | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/react/filterPanel/src/Example/FilterPanel.tsx b/react/filterPanel/src/Example/FilterPanel.tsx index 4ff607a..86bcaf7 100644 --- a/react/filterPanel/src/Example/FilterPanel.tsx +++ b/react/filterPanel/src/Example/FilterPanel.tsx @@ -350,6 +350,16 @@ export const FilterPanel = () => { duration: 0.25, } ); + animate( + `#${tableId} .cds--pagination`, + { + width: '100%', + transform: 'translateX(0px)', + }, + { + duration: 0.25, + } + ); } else { animate( `#${tableId} .panel--container`, @@ -371,6 +381,16 @@ export const FilterPanel = () => { duration: 0.25, } ); + animate( + `#${tableId} .cds--pagination`, + { + width: 'calc(100% - 336px)', + transform: 'translateX(336px)', + }, + { + duration: 0.25, + } + ); } }; diff --git a/react/filterPanel/src/Example/example.scss b/react/filterPanel/src/Example/example.scss index 2c64ccb..a8bb13b 100644 --- a/react/filterPanel/src/Example/example.scss +++ b/react/filterPanel/src/Example/example.scss @@ -91,7 +91,7 @@ .panel--container { min-height: calc( - var(--table-height) + 16px + var(--table-height) + 40px ); // account for x scrollbar of table min-width: 320px; @@ -124,7 +124,7 @@ overflow-y: scroll; position: relative; width: 100%; - height: calc(var(--table-height) + 16px); + height: calc(var(--table-height) + 40px); .filter--panel__close-wrapper.cds--popover-container:not( .cds--popover--auto-align From d827b4a76dcdf4bd92b1583e7b04f9759f569993 Mon Sep 17 00:00:00 2001 From: Matt Gallo Date: Thu, 6 Feb 2025 11:24:22 -0500 Subject: [PATCH 3/3] chore: fix styles of empty state with pagination --- react/filterPanel/src/Example/example.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/react/filterPanel/src/Example/example.scss b/react/filterPanel/src/Example/example.scss index a8bb13b..f488fe6 100644 --- a/react/filterPanel/src/Example/example.scss +++ b/react/filterPanel/src/Example/example.scss @@ -193,12 +193,22 @@ color: $text-inverse; } +.tanstack-example .cds--data-table tbody.empty-table-body tr { + height: 14rem; +} + +.tanstack-example .cds--data-table tbody.empty-table-body tr td { + display: flex; + align-items: start; + padding-top: 2rem; + border-block-end: 0; +} + .filter--summary-tag-and-overflow-wrapper { display: flex; align-items: center; } -.cds--data-table.empty-table-wrapper td, .cds--data-table.empty-table-wrapper tbody th, .cds--data-table.empty-table-wrapper tbody tr:hover td { border-block-end: 0;