From f7bb289e2c9edfd8afab5ccbf604d7bced7fdbab Mon Sep 17 00:00:00 2001 From: fzaninotto Date: Fri, 8 Jul 2022 17:01:06 +0200 Subject: [PATCH] Fix cannot be used outside a ListContext Closes #7955 --- .../list/useListPaginationContext.ts | 59 ++++++++++++------- .../src/list/pagination/Pagination.spec.tsx | 29 +++++++++ .../src/list/pagination/Pagination.tsx | 5 +- 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/packages/ra-core/src/controller/list/useListPaginationContext.ts b/packages/ra-core/src/controller/list/useListPaginationContext.ts index 1c0ccce3d00..0a7ad0f4e41 100644 --- a/packages/ra-core/src/controller/list/useListPaginationContext.ts +++ b/packages/ra-core/src/controller/list/useListPaginationContext.ts @@ -1,4 +1,5 @@ -import { useContext } from 'react'; +import { useContext, useMemo } from 'react'; +import defaults from 'lodash/defaults'; import { ListPaginationContext, @@ -29,25 +30,43 @@ export const useListPaginationContext = ( props?: any ): ListPaginationContextValue => { const context = useContext(ListPaginationContext); - if (!context.setPage) { - /** - * The element isn't inside a - * - * This may only happen when using Datagrid / SimpleList / SingleFieldList components - * outside of a List / ReferenceManyField / ReferenceArrayField - - * which isn't documented but tolerated. - * To avoid breakage in that case, fallback to props - * - * @deprecated - to be removed in 4.0 - */ - if (process.env.NODE_ENV !== 'production') { - console.log( - "List components must be used inside a . Relying on props rather than context to get List data and callbacks is deprecated and won't be supported in the next major version of react-admin." - ); - } - return props; - } - return context; + return useMemo( + () => + defaults( + {}, + props != null ? extractListPaginationContextProps(props) : {}, + context + ), + [context, props] + ); }; +/** + * Extract only the list controller props + * + * @param {Object} props Props passed to the useListContext hook + * + * @returns {ListControllerResult} List controller props + */ +const extractListPaginationContextProps = ({ + isLoading, + page, + perPage, + setPage, + setPerPage, + hasPreviousPage, + hasNextPage, + total, + resource, +}) => ({ + isLoading, + page, + perPage, + setPage, + setPerPage, + hasPreviousPage, + hasNextPage, + total, + resource, +}); export default useListPaginationContext; diff --git a/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx b/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx index d974083dbbb..4225ff06b69 100644 --- a/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx +++ b/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx @@ -323,4 +323,33 @@ describe('', () => { ).not.toBeNull(); }); }); + + it('should work outside of a ListContext', () => { + render( + + null} + isLoading={false} + setPerPage={() => {}} + hasNextPage={undefined} + hasPreviousPage={undefined} + perPage={1} + total={2} + page={1} + rowsPerPageOptions={[1]} + /> + + ); + const nextButton = screen.queryByLabelText( + 'ra.navigation.next' + ) as HTMLButtonElement; + expect(nextButton).not.toBeNull(); + expect(nextButton.disabled).toBe(false); + const prevButton = screen.queryByLabelText( + 'ra.navigation.previous' + ) as HTMLButtonElement; + expect(prevButton).not.toBeNull(); + expect(prevButton.disabled).toBe(true); + }); }); diff --git a/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx b/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx index 3a5378c4686..dac7907c7ff 100644 --- a/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx +++ b/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx @@ -13,6 +13,7 @@ import { useListPaginationContext, sanitizeListRestProps, ComponentPropType, + ListPaginationContextValue, } from 'ra-core'; import { PaginationActions } from './PaginationActions'; @@ -152,7 +153,9 @@ const DefaultLimit = ; const DefaultRowsPerPageOptions = [5, 10, 25]; const emptyArray = []; -export interface PaginationProps extends TablePaginationBaseProps { +export interface PaginationProps + extends TablePaginationBaseProps, + Partial { rowsPerPageOptions?: number[]; actions?: FC; limit?: ReactElement;