-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add <InfiniteList>
and <InfinitePagination>
components
#8781
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
9bc9a2e
Add infinite list core hooks and components
fzaninotto 15382dc
Add story to test base case
fzaninotto 7e6d5a6
Add InfiniteList component
fzaninotto df9567a
Remove need for i18n
fzaninotto c82e0dc
Test more props in storybook
fzaninotto e5952d9
Rename basic story
fzaninotto f821f03
Rename InfiniteScroll to InfinitePagination
fzaninotto 5a55eee
Do not set sort by default on the controller
fzaninotto 8635bb4
Use InfiniteList in simple example
fzaninotto 86d676c
Fix useUpdate optimistic effect with infinite list
fzaninotto de8e225
Update useGetMany to update getInfiniteList cache
fzaninotto f344252
Add tests for useUpdate and useUpdateMany
fzaninotto 71e57f8
Update useDelete optimistic for getInfiniteList
fzaninotto b138935
update useDeleteMany cache
fzaninotto 4aefedd
Add documentation
fzaninotto dc754b4
Document how to show the count
fzaninotto 92dbfa4
Add mention of sticky header and footer in the docs
fzaninotto 0d0baa0
Fix sidebar starts open on Mobile
fzaninotto 6a9cb27
Add e2e test for infinite pagination
fzaninotto ba6a82e
Add unit test for InfiniteListBase
fzaninotto 0972010
Fix total when getList returns a pageInfo
fzaninotto c84f432
Update packages/ra-core/src/controller/list/useInfiniteListController.ts
fzaninotto 81ad790
Update packages/ra-core/src/controller/list/useInfiniteListController.ts
fzaninotto 4f5c884
Fix InfiniteList example
fzaninotto 687bbff
Add entries in Reference
fzaninotto aa6266c
Add load more story and documentation
fzaninotto dacbbe6
Fix bug where the InfinitePagination cannot fetch after page 2
fzaninotto 5345571
Fix InfiniteList stories share the same list state
fzaninotto 1a25332
Merge branch 'next' into infinite-list
fzaninotto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import listPageFactory from '../support/ListPage'; | ||
|
||
describe('Mobile UI', () => { | ||
const ListPagePosts = listPageFactory('/#/posts'); | ||
|
||
beforeEach(() => { | ||
window.localStorage.clear(); | ||
cy.viewport('iphone-x'); | ||
}); | ||
|
||
describe('Infinite Scroll', () => { | ||
it.only('should load more items when scrolling to the bottom of the page', () => { | ||
ListPagePosts.navigate(); | ||
cy.contains('Sed quo et et fugiat modi').should('not.exist'); | ||
cy.scrollTo('bottom'); | ||
cy.wait(500); | ||
cy.scrollTo('bottom'); | ||
cy.contains('Sed quo et et fugiat modi'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
--- | ||
layout: default | ||
title: "The InfiniteList Component" | ||
--- | ||
|
||
# `<InfiniteList>` | ||
|
||
The `<InfiniteList>` component is an alternative to [the `<List>` component](./List.md) that allows user to load more records when they scroll to the bottom of the list. It's useful when you have a large number of records, or when users are using a mobile device. | ||
|
||
<video controls autoplay muted loop width="100%"> | ||
<source src="./img/infinite-book-list.webm" poster="./img/infinite-book-list.webp" type="video/webm"> | ||
Your browser does not support the video tag. | ||
</video> | ||
|
||
`<InfiniteList>` fetches the list of records from the data provider, and renders the default list layout (title, buttons, filters). It delegates the rendering of the list of records to its child component. Usually, it's a [`<Datagrid>`](./Datagrid.md) or a [`<SimpleList>`](./SimpleList.md), responsible for displaying a table with one row for each record. | ||
|
||
## Usage | ||
|
||
Here is the minimal code necessary to display a list of books with infinite scroll: | ||
|
||
```jsx | ||
// in src/books.js | ||
import { InfiniteList, Datagrid, TextField, DateField } from 'react-admin'; | ||
|
||
export const BookList = () => ( | ||
<InfiniteList> | ||
<Datagrid> | ||
<TextField source="id" /> | ||
<TextField source="title" /> | ||
<DateField source="author" /> | ||
</Datagrid> | ||
</InfiniteList> | ||
); | ||
|
||
// in src/App.js | ||
import { Admin, Resource } from 'react-admin'; | ||
import jsonServerProvider from 'ra-data-json-server'; | ||
|
||
import { BookList } from './books'; | ||
|
||
const App = () => ( | ||
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}> | ||
<Resource name="books" list={BookList} /> | ||
</Admin> | ||
); | ||
|
||
export default App; | ||
``` | ||
|
||
That's enough to display a basic post list, that users can sort and filter, and load additional records when they reach the bottom of the list. | ||
|
||
**Tip**: `<Datagrid>` has a sticky header by default, so the user can always see the column names when they scroll down. | ||
|
||
## Props | ||
|
||
The props are the same as [the `<List>` component](./List.md): | ||
|
||
| Prop | Required | Type | Default | Description | | ||
|----------------------------|----------|----------------|-------------------------|----------------------------------------------------------------------------------------------| | ||
| `children` | Required | `ReactNode` | - | The component to use to render the list of records. | | ||
| `actions` | Optional | `ReactElement` | - | The actions to display in the toolbar. | | ||
| `aside` | Optional | `ReactElement` | - | The component to display on the side of the list. | | ||
| `component` | Optional | `Component` | `Card` | The component to render as the root element. | | ||
| `debounce` | Optional | `number` | `500` | The debounce delay in milliseconds to apply when users change the sort or filter parameters. | | ||
| `disable Authentication` | Optional | `boolean` | `false` | Set to `true` to disable the authentication check. | | ||
| `disable SyncWithLocation` | Optional | `boolean` | `false` | Set to `true` to disable the synchronization of the list parameters with the URL. | | ||
| `empty` | Optional | `ReactElement` | - | The component to display when the list is empty. | | ||
| `empty WhileLoading` | Optional | `boolean` | `false` | Set to `true` to return `null` while the list is loading. | | ||
| `exporter` | Optional | `function` | - | The function to call to export the list. | | ||
| `filters` | Optional | `ReactElement` | - | The filters to display in the toolbar. | | ||
| `filter` | Optional | `object` | - | The permanent filter values. | | ||
| `filter DefaultValues` | Optional | `object` | - | The default filter values. | | ||
| `hasCreate` | Optional | `boolean` | `false` | Set to `true` to show the create button. | | ||
| `pagination` | Optional | `ReactElement` | `<Infinite Pagination>` | The pagination component to use. | | ||
| `perPage` | Optional | `number` | `10` | The number of records to fetch per page. | | ||
| `queryOptions` | Optional | `object` | - | The options to pass to the `useQuery` hook. | | ||
| `resource` | Optional | `string` | - | The resource name, e.g. `posts`. | | ||
| `sort` | Optional | `object` | - | The initial sort parameters. | | ||
| `storeKey` | Optional | `string` | - | The key to use to store the current filter & sort. | | ||
| `title` | Optional | `string` | - | The title to display in the App Bar. | | ||
| `sx` | Optional | `object` | - | The CSS styles to apply to the component. | | ||
|
||
Check the [`<List>` component](./List.md) for details about each prop. | ||
|
||
Additional props are passed down to the root component (a MUI `<Card>` by default). | ||
|
||
## `pagination` | ||
|
||
You can replace the default "load on scroll" pagination (triggered by a component named `<InfinitePagination>`) by a custom pagination component. To get the pagination state and callbacks, you'll need to read the `InfinitePaginationContext`. | ||
|
||
![load more button](./img/infinite-pagination-load-more.webp) | ||
|
||
For example, here is a custom infinite pagination component displaying a "Load More" button at the bottom of the list: | ||
|
||
```jsx | ||
import { InfiniteList, useInfinitePaginationContext, Datagrid, TextField } from 'react-admin'; | ||
import { Box, Button } from '@mui/material'; | ||
|
||
const LoadMore = () => { | ||
const { | ||
hasNextPage, | ||
fetchNextPage, | ||
isFetchingNextPage, | ||
} = useInfinitePaginationContext(); | ||
return hasNextPage ? ( | ||
<Box mt={1} textAlign="center"> | ||
<Button | ||
disabled={isFetchingNextPage} | ||
onClick={() => fetchNextPage()} | ||
> | ||
Load more | ||
</Button> | ||
</Box> | ||
) : null; | ||
}; | ||
|
||
export const BookList = () => ( | ||
<InfiniteList pagination={<LoadMore />}> | ||
<Datagrid> | ||
<TextField source="id" /> | ||
<TextField source="title" /> | ||
<TextField source="author" /> | ||
</Datagrid> | ||
</InfiniteList> | ||
); | ||
``` | ||
|
||
## Showing The Record Count | ||
|
||
One drawback of the `<InfiniteList>` component is that it doesn't show the number of results. To fix this, you can use `useListContext` to access the `total` property of the list, and render the total number of results in a sticky footer: | ||
|
||
![Infinite list with total number of results](./img/infinite-pagination-count.webp) | ||
|
||
{% raw %} | ||
```jsx | ||
import { useListContext, InfinitePagination, InfiniteList } from 'react-admin'; | ||
import { Box, Card, Typography } from '@mui/material'; | ||
|
||
const CustomPagination = () => { | ||
const { total } = useListContext(); | ||
return ( | ||
<> | ||
<InfinitePagination /> | ||
{total > 0 && ( | ||
<Box position="sticky" bottom={0} textAlign="center"> | ||
<Card | ||
elevation={2} | ||
sx={{ px: 2, py: 1, mb: 1, display: 'inline-block' }} | ||
> | ||
<Typography variant="body2">{total} results</Typography> | ||
</Card> | ||
</Box> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export const BookList = () => ( | ||
<InfiniteList pagination={<CustomPagination />}> | ||
// ... | ||
</InfiniteList> | ||
); | ||
``` | ||
{% endraw %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make a component out of this too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I don't think this is a general solution, it's just an example. People will want to put the number of results on top of the list, in a datagrid footer, in the action bar, etc... There is no standard for that UI.