diff --git a/docs/useList.md b/docs/useList.md
index aff6c512d1d..d9085a2a62b 100644
--- a/docs/useList.md
+++ b/docs/useList.md
@@ -242,3 +242,21 @@ const {
refetch, // a function that throws an error, as refetch doesn't make sense for local data
} = getGetList({ data });
```
+## `filterCallback`
+
+Property for custom filter definition. Being able to apply more complex filters using operators
+
+```jsx
+const { data } = useList({
+ data: [
+ { id: 1, name: 'Arnold' },
+ { id: 2, name: 'Sylvester' },
+ { id: 3, name: 'Jean-Claude' },
+ ],
+ sort: { field: 'name', order: 'ASC' },
+ filterCallback: (record) => record.id > 1 && record.name !== 'Jean-Claude'
+});
+// data will be
+// [
+// { id: 2, name: 'Sylvester' },
+// ]
\ No newline at end of file
diff --git a/packages/ra-core/src/controller/list/useList.spec.tsx b/packages/ra-core/src/controller/list/useList.spec.tsx
index 41056bea0c7..7c0c3b6b7bb 100644
--- a/packages/ra-core/src/controller/list/useList.spec.tsx
+++ b/packages/ra-core/src/controller/list/useList.spec.tsx
@@ -228,4 +228,39 @@ describe('', () => {
})
);
});
+
+ it('should filter array data based on the custom filter', async () => {
+ const callback = jest.fn();
+ const data = [
+ { id: 1, items: ['one', 'two'] },
+ { id: 2, items: ['three'] },
+ { id: 3, items: 'four' },
+ { id: 4, items: ['five'] },
+ ];
+
+ render(
+ record.id > 2}
+ callback={callback}
+ />
+ );
+
+ await waitFor(() => {
+ expect(callback).toHaveBeenCalledWith(
+ expect.objectContaining({
+ sort: { field: 'id', order: 'ASC' },
+ isFetching: false,
+ isLoading: false,
+ data: [
+ { id: 3, items: 'four' },
+ { id: 4, items: ['five'] },
+ ],
+ error: undefined,
+ total: 2,
+ })
+ );
+ });
+ });
});
diff --git a/packages/ra-core/src/controller/list/useList.ts b/packages/ra-core/src/controller/list/useList.ts
index a5b3576edff..fa4bb711486 100644
--- a/packages/ra-core/src/controller/list/useList.ts
+++ b/packages/ra-core/src/controller/list/useList.ts
@@ -48,6 +48,7 @@ const refetch = () => {
* @param {Number} props.page: Optional. The initial page index
* @param {Number} props.perPage: Optional. The initial page size
* @param {SortPayload} props.sort: Optional. The initial sort (field and order)
+ * @param {filterCallback} prop.filterCallback Optional. A function that allows you to make a custom filter
*/
export const useList = (
props: UseListOptions
@@ -61,6 +62,7 @@ export const useList = (
page: initialPage = 1,
perPage: initialPerPage = 1000,
sort: initialSort,
+ filterCallback = (record: RecordType) => Boolean(record),
} = props;
const resource = useResourceContext(props);
@@ -161,23 +163,25 @@ export const useList = (
// 1. filter
if (filterValues) {
- tempData = data.filter(record =>
- Object.entries(filterValues).every(
- ([filterName, filterValue]) => {
- const recordValue = get(record, filterName);
- const result = Array.isArray(recordValue)
- ? Array.isArray(filterValue)
- ? recordValue.some(item =>
- filterValue.includes(item)
- )
- : recordValue.includes(filterValue)
- : Array.isArray(filterValue)
- ? filterValue.includes(recordValue)
- : filterValue == recordValue; // eslint-disable-line eqeqeq
- return result;
- }
+ tempData = data
+ .filter(record =>
+ Object.entries(filterValues).every(
+ ([filterName, filterValue]) => {
+ const recordValue = get(record, filterName);
+ const result = Array.isArray(recordValue)
+ ? Array.isArray(filterValue)
+ ? recordValue.some(item =>
+ filterValue.includes(item)
+ )
+ : recordValue.includes(filterValue)
+ : Array.isArray(filterValue)
+ ? filterValue.includes(recordValue)
+ : filterValue == recordValue; // eslint-disable-line eqeqeq
+ return result;
+ }
+ )
)
- );
+ .filter(filterCallback);
}
const filteredLength = tempData.length;
@@ -269,6 +273,7 @@ export interface UseListOptions {
perPage?: number;
sort?: SortPayload;
resource?: string;
+ filterCallback?: (record: RecordType) => boolean;
}
export type UseListValue<