Skip to content

Commit eff8fa2

Browse files
authored
SW-8109: New Tables: Add "Clear All" feature to Filters
1 parent 3fbe35e commit eff8fa2

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

src/components/EditableTable/index.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
useMaterialReactTable,
1515
} from 'material-react-table';
1616

17+
import Button from '../Button/Button';
18+
1719
export type ColumnEditConfig<TData extends Record<string, any>> = {
1820
/** Function to call when a cell value is saved (on blur) */
1921
onSave?: (row: TData, value: any, columnId: string) => void | Promise<void>;
@@ -59,10 +61,14 @@ export type EditableTableColumn<TData extends Record<string, any>> = {
5961
};
6062

6163
export type EditableTableProps<TData extends Record<string, any>> = {
64+
/** Label for the "Clear All Filters" button */
65+
clearAllFiltersLabel?: string;
6266
/** Array of column definitions */
6367
columns: EditableTableColumn<TData>[];
6468
/** Array of data rows */
6569
data: TData[];
70+
/** Whether to show a "Clear All Filters" button when filters are active (default: true) */
71+
enableClearAllFilters?: boolean;
6672
/** Whether to enable editing (default: false) */
6773
enableEditing?: boolean;
6874
/** Whether to enable column ordering (default: false) */
@@ -86,6 +92,8 @@ export type EditableTableProps<TData extends Record<string, any>> = {
8692
/** Whether to show the top toolbar (default: true) */
8793
enableTopToolbar?: boolean;
8894
initialSorting?: { id: string; desc: boolean }[];
95+
/** Callback fired after all filters are cleared */
96+
onClearAllFilters?: () => void;
8997
/** Callback when a row is clicked */
9098
onRowClick?: (row: TData) => void;
9199
/** Custom toolbar actions */
@@ -96,8 +104,10 @@ export type EditableTableProps<TData extends Record<string, any>> = {
96104
};
97105

98106
export default function EditableTable<TData extends Record<string, any>>({
107+
clearAllFiltersLabel,
99108
columns,
100109
data,
110+
enableClearAllFilters = true,
101111
enableEditing = false,
102112
enableColumnOrdering = false,
103113
enableColumnPinning = false,
@@ -110,13 +120,17 @@ export default function EditableTable<TData extends Record<string, any>>({
110120
enableBottomToolbar = true,
111121
enableTopToolbar = true,
112122
initialSorting,
123+
onClearAllFilters,
113124
onRowClick,
114125
renderToolbarInternalActions,
115126
sx,
116127
tableOptions = {},
117128
}: EditableTableProps<TData>): JSX.Element {
118129
const theme = useTheme();
119130

131+
const filtersEnabled = enableColumnFilters || enableGlobalFilter;
132+
const showClearAllFilters = enableClearAllFilters && clearAllFiltersLabel && filtersEnabled;
133+
120134
// Sticky column filters - load from localStorage (only if stickyFilters is enabled)
121135
const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(() => {
122136
if (!stickyFilters || !storageKey) {
@@ -274,6 +288,23 @@ export default function EditableTable<TData extends Record<string, any>>({
274288
...(enableGlobalFilter ? { showGlobalFilter: true } : {}),
275289
};
276290

291+
const handleClearAllFilters = useCallback(
292+
(tbl: MRT_TableInstance<TData>) => {
293+
tbl.resetColumnFilters();
294+
if (enableGlobalFilter) {
295+
tbl.resetGlobalFilter();
296+
}
297+
if (stickyFilters && storageKey) {
298+
setColumnFilters([]);
299+
localStorage.removeItem(`${storageKey}_columnFilters`);
300+
}
301+
onClearAllFilters?.();
302+
},
303+
[enableGlobalFilter, stickyFilters, storageKey, onClearAllFilters]
304+
);
305+
306+
const consumerRenderTopToolbarCustomActions = tableOptions?.renderTopToolbarCustomActions;
307+
277308
const table = useMaterialReactTable({
278309
columns: mrtColumns,
279310
data,
@@ -313,6 +344,31 @@ export default function EditableTable<TData extends Record<string, any>>({
313344
}),
314345
// Merge any additional table options
315346
...tableOptions,
347+
// Compose renderTopToolbarCustomActions with clear-all button
348+
...(showClearAllFilters || consumerRenderTopToolbarCustomActions
349+
? {
350+
renderTopToolbarCustomActions: ({ table: tbl }: { table: MRT_TableInstance<TData> }) => {
351+
const state = tbl.getState();
352+
const hasColumnFilters = state.columnFilters.length > 0;
353+
const hasGlobalFilter = enableGlobalFilter && !!state.globalFilter;
354+
const hasActiveFilters = hasColumnFilters || hasGlobalFilter;
355+
356+
return (
357+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
358+
{consumerRenderTopToolbarCustomActions?.({ table: tbl })}
359+
{showClearAllFilters && hasActiveFilters && (
360+
<Button
361+
onClick={() => handleClearAllFilters(tbl)}
362+
label={clearAllFiltersLabel}
363+
priority='ghost'
364+
size='small'
365+
/>
366+
)}
367+
</Box>
368+
);
369+
},
370+
}
371+
: {}),
316372
// Add sticky filters state if enabled
317373
...(stickyFilters && storageKey
318374
? {

src/stories/EditableTable.stories.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,121 @@ export const WithFilters: Story = () => {
309309
);
310310
};
311311

312+
export const WithClearAllFilters: Story = () => {
313+
const rowCount = 50;
314+
const [data, setData] = useState<RowData[]>(() => {
315+
return Array(rowCount)
316+
.fill(null)
317+
.map((_, j) => {
318+
if (j % 2 === 0) {
319+
return {
320+
id: `${j}`,
321+
name: `Constanza_${j}`,
322+
middlename: '',
323+
lastname: 'Uanini',
324+
occupation: 'Artist',
325+
date: '2023-02-03',
326+
pets: 5,
327+
available: 'yes',
328+
};
329+
} else if (j % 3 === 0) {
330+
return {
331+
id: `${j}`,
332+
name: `Carlos_${j}`,
333+
middlename: '--',
334+
lastname: 'Thurber',
335+
occupation: 'Freelancer',
336+
available: 'no',
337+
date: '2023-04-12',
338+
pets: 10,
339+
};
340+
} else {
341+
return {
342+
id: `${j}`,
343+
name: `Jane${j}`,
344+
middlename: 'John',
345+
lastname: 'Doe',
346+
occupation: 'Business analyst',
347+
available: 'yes',
348+
date: '2023-04-27',
349+
pets: 12,
350+
previousStudies: 'Natural Sciences, Social Sciences',
351+
};
352+
}
353+
});
354+
});
355+
356+
const columns = useMemo<EditableTableColumn<RowData>[]>(
357+
() => [
358+
{
359+
id: 'name',
360+
header: 'Name',
361+
accessorKey: 'name',
362+
size: 150,
363+
filterVariant: 'text',
364+
editConfig: {
365+
onSave: (row, value, columnId) => {
366+
setData((prev) => prev.map((item) => (item.id === row.id ? { ...item, [columnId]: value } : item)));
367+
},
368+
},
369+
},
370+
{
371+
id: 'occupation',
372+
header: 'Occupation',
373+
accessorKey: 'occupation',
374+
size: 180,
375+
filterVariant: 'select',
376+
filterSelectOptions: ['Artist', 'Freelancer', 'Business analyst', 'Developer', 'Designer'],
377+
editConfig: {
378+
editVariant: 'select',
379+
selectOptions: [
380+
{ label: 'Artist', value: 'Artist' },
381+
{ label: 'Freelancer', value: 'Freelancer' },
382+
{ label: 'Business analyst', value: 'Business analyst' },
383+
{ label: 'Developer', value: 'Developer' },
384+
{ label: 'Designer', value: 'Designer' },
385+
],
386+
onSave: (row, value, columnId) => {
387+
setData((prev) => prev.map((item) => (item.id === row.id ? { ...item, [columnId]: value } : item)));
388+
},
389+
},
390+
},
391+
{
392+
id: 'available',
393+
header: 'Available',
394+
accessorKey: 'available',
395+
size: 120,
396+
filterVariant: 'select',
397+
filterSelectOptions: ['yes', 'no'],
398+
editConfig: {
399+
editVariant: 'select',
400+
selectOptions: [
401+
{ label: 'Yes', value: 'yes' },
402+
{ label: 'No', value: 'no' },
403+
],
404+
onSave: (row, value, columnId) => {
405+
setData((prev) => prev.map((item) => (item.id === row.id ? { ...item, [columnId]: value } : item)));
406+
},
407+
},
408+
},
409+
],
410+
[]
411+
);
412+
413+
return (
414+
<EditableTable<RowData>
415+
clearAllFiltersLabel='Clear All Filters'
416+
columns={columns}
417+
data={data}
418+
enableColumnFilters={true}
419+
enableGlobalFilter={true}
420+
enablePagination={true}
421+
enableSorting={true}
422+
onClearAllFilters={() => console.log('All filters cleared')}
423+
/>
424+
);
425+
};
426+
312427
export const WithOccupationFilter: Story = () => {
313428
const rowCount = 50;
314429
const [data, setData] = useState<RowData[]>(() => {

0 commit comments

Comments
 (0)