Skip to content
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

Fix <ListView> should show custom empty component with partial pagination #8945

Merged
merged 5 commits into from
May 30, 2023

Conversation

yanchesky
Copy link
Contributor

The logic that checks whether the custom empty component should be shown relies on the total value. Since react-admin supports partial pagination, it should handle that case too. This PR fixes that.

Copy link
Contributor

@slax57 slax57 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks!

Co-authored-by: Jean-Baptiste Kaiser <jbaptiste.kaiser@gmail.com>
@yanchesky
Copy link
Contributor Author

Removed propTypes as you asked. I'm not sure though if I did not remove too many of them. Some of them are passed to the useListContext. I left only those which are directly consumed by

Also, I fixed the condition you proposed as it broke the test. Btw what's wrong with using optional chaining?

@slax57
Copy link
Contributor

slax57 commented May 26, 2023

Thanks for cleaning out the propTypes 🙂

Regarding the condition, it was not about the conditional chaining which is perfectly fine to use.
It's just that I would expect the empty page to be displayed also in case data = undefined.

With the original condition, when data = undefined, data?.length === 0 will return false, and so the empty page will not be displayed, which is not what I'd expect.

Hence I suggested (!data || data.length === 0), which will return true in this case.

Note that your latest version !!data && data.length === 0 also won't work as it returns false.

Can you fix it?
Thanks 🙂

@yanchesky
Copy link
Contributor Author

In that case, your suggestion indeed makes sense. However, as I said, returning a custom component in case data = undefined or null breaks the test. Here is failed test run and below is the implementation of the test that fails. We can see that the returned response contains only error and the assertion is to render the error component.

it('should render a list page with an error message when there is an error', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const Datagrid = () => <div>datagrid</div>;
const dataProvider = {
getList: jest.fn(() =>
Promise.reject({ error: { key: 'error.unknown' } })
),
} as any;
render(
<CoreAdminContext dataProvider={dataProvider}>
<ThemeProvider theme={theme}>
<List resource="posts">
<Datagrid />
</List>
</ThemeProvider>
</CoreAdminContext>
);
await waitFor(() => {
expect(screen.getByText('ra.page.error'));
});
});

The error component is rendered by the renderList function rather than renderEmpty, thus we intend shouldRenderEmptyPage to be false when data = undefined.
{shouldRenderEmptyPage ? renderEmpty() : renderList()}

const renderList = () => (
<div className={ListClasses.main}>
{(filters || actions) && (
<ListToolbar
filters={filters}
actions={actions}
hasCreate={hasCreate}
/>
)}
<Content className={ListClasses.content}>
{bulkActionButtons &&
children &&
React.isValidElement<any>(children)
? // FIXME remove in 5.0
cloneElement(children, {
bulkActionButtons,
})
: children}
</Content>
{error ? (
<Error error={error} resetErrorBoundary={null} />
) : (
pagination !== false && pagination
)}
</div>
);

IMO this is the correct approach. The case when isLoading = false and data = undefined seems like an error to me. Also, dataProvider expects data to be somehow defined, which validateResponseFormat.ts confirms. I would stay then with my first solution as it did the job. Any other solution would require more refactoring (Which I'm willing to do if you would like so 😊).

Also speaking of validateResponseFormat.ts a while ago I noticed that the error produced by this function is not accurate. This may be misleading for new users, because the actual response may be { data: [...], total: 123 } or { data: [...], pageInfo: {...} } . Would you like me to fix this in this PR, create a new one or leave it as is?

if (
fetchActionsWithTotalResponse.includes(type) &&
!response.hasOwnProperty('total') &&
!response.hasOwnProperty('pageInfo')
) {
logger(
`The response to '${type}' must be like { data: [...], total: 123 }, but the received response does not have a 'total' key. The dataProvider is probably wrong for '${type}'`
);
throw new Error('ra.notification.data_provider_error');
}

@slax57
Copy link
Contributor

slax57 commented May 30, 2023

Okay. I think you're right. Thanks for the detailed answer 🙂
We should keep your initial implementation then. I'll revert my suggested change.

Regarding the issue with validateResponseFormat.ts, this is a good point.
This would deserve its own PR since it's not linked to this fix. Can you open a new one?
Thank you so much 🙂

@slax57 slax57 added this to the 4.11.1 milestone May 30, 2023
@slax57 slax57 merged commit 0d28e66 into marmelab:master May 30, 2023
@slax57 slax57 changed the title Fix show custom empty component on <ListView> Fix <ListView> should show custom empty component with partial pagination May 30, 2023
@yanchesky
Copy link
Contributor Author

Regarding the issue with validateResponseFormat.ts, this is a good point. This would deserve its own PR since it's not linked to this fix. Can you open a new one? Thank you so much 🙂

Sure. I'll do that today evening.

fzaninotto added a commit that referenced this pull request Sep 10, 2024
## Problem

The default empty list page, which is an invite to create new elements, appears even when some elements already exist.

Here is a repro that can be tested in the simple example:

1. For to the post list (in a large screen to show the datagrid version)
2. Select all rows using the top left checkbox
3. Hit the bulk delete button

The empty page shows up, even though there are remaining posts (in the following page).

## Cause

This bug was introduced by #8945, which fixed a bug with partial pagination.

The condition for displaying the empty page is wrong:

https://github.com/marmelab/react-admin/blob/e6129b2d1aded97f55bf40bf1d5602cc42a03113/packages/ra-ui-materialui/src/list/ListView.tsx#L60-L64
fzaninotto added a commit that referenced this pull request Sep 10, 2024
The default empty list page, which is an invite to create new elements, appears even when some elements already exist.

Here is a repro that can be tested in the simple example:

1. Go to the post list (in a large screen to show the datagrid version)
2. Select all rows using the top left checkbox
3. Hit the bulk delete button

The empty page shows up, even though there are remaining posts (in the following page).

This bug was introduced by #8945, which fixed a bug with partial pagination.

The condition for displaying the empty page is wrong:

https://github.com/marmelab/react-admin/blob/e6129b2d1aded97f55bf40bf1d5602cc42a03113/packages/ra-ui-materialui/src/list/ListView.tsx#L60-L64
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants