Skip to content

Commit

Permalink
Fix <Pagination> cannot be used outside a ListContext
Browse files Browse the repository at this point in the history
Closes #7955
  • Loading branch information
fzaninotto committed Jul 8, 2022
1 parent 84306af commit f7bb289
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 21 deletions.
59 changes: 39 additions & 20 deletions packages/ra-core/src/controller/list/useListPaginationContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from 'react';
import { useContext, useMemo } from 'react';
import defaults from 'lodash/defaults';

import {
ListPaginationContext,
Expand Down Expand Up @@ -29,25 +30,43 @@ export const useListPaginationContext = (
props?: any
): ListPaginationContextValue => {
const context = useContext(ListPaginationContext);
if (!context.setPage) {
/**
* The element isn't inside a <ListPaginationContext.Provider>
*
* 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 <ListContextProvider>. 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;
29 changes: 29 additions & 0 deletions packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,33 @@ describe('<Pagination />', () => {
).not.toBeNull();
});
});

it('should work outside of a ListContext', () => {
render(
<ThemeProvider theme={theme}>
<Pagination
resource="posts"
setPage={() => null}
isLoading={false}
setPerPage={() => {}}
hasNextPage={undefined}
hasPreviousPage={undefined}
perPage={1}
total={2}
page={1}
rowsPerPageOptions={[1]}
/>
</ThemeProvider>
);
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);
});
});
5 changes: 4 additions & 1 deletion packages/ra-ui-materialui/src/list/pagination/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useListPaginationContext,
sanitizeListRestProps,
ComponentPropType,
ListPaginationContextValue,
} from 'ra-core';

import { PaginationActions } from './PaginationActions';
Expand Down Expand Up @@ -152,7 +153,9 @@ const DefaultLimit = <PaginationLimit />;
const DefaultRowsPerPageOptions = [5, 10, 25];
const emptyArray = [];

export interface PaginationProps extends TablePaginationBaseProps {
export interface PaginationProps
extends TablePaginationBaseProps,
Partial<ListPaginationContextValue> {
rowsPerPageOptions?: number[];
actions?: FC;
limit?: ReactElement;
Expand Down

0 comments on commit f7bb289

Please sign in to comment.