Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions packages/ra-core/src/dataTable/DataTableBase.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen } from '@testing-library/react';
import { DataTableBase } from './DataTableBase';
import { ResourceContextProvider, useResourceContext } from '../core';
import { ListContextProvider } from '../controller';

describe('<DataTableBase>', () => {
const defaultListContext = {
resource: 'posts',
data: [],
total: 0,
isPending: false,
isFetching: false,
sort: { field: 'id', order: 'ASC' },
};

it('should render children when data is present', () => {
render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={
{
...defaultListContext,
data: [{ id: 1 }],
total: 1,
} as any
}
>
<DataTableBase
empty={<div>Empty</div>}
loading={<div>Loading</div>}
hasBulkActions={false}
>
<div>Child Content</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Child Content')).not.toBeNull();
expect(screen.queryByText('Loading')).toBeNull();
expect(screen.queryByText('Empty')).toBeNull();
});

it('should render empty component when no data', () => {
render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={{ ...defaultListContext, data: [], total: 0 } as any}
>
<DataTableBase
empty={<div>Empty Component</div>}
loading={<div>Loading</div>}
hasBulkActions={false}
>
<div>Child</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Empty Component')).not.toBeNull();
expect(screen.queryByText('Child')).toBeNull();
});

it('should render loading component when pending', () => {
render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={{ ...defaultListContext, isPending: true } as any}
>
<DataTableBase
empty={<div>Empty</div>}
loading={<div>Loading Component</div>}
hasBulkActions={false}
>
<div>Child</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Loading Component')).not.toBeNull();
expect(screen.queryByText('Child')).toBeNull();
});

it('should display the empty component instead of the table header when loaded with no data', () => {
render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={
{
...defaultListContext,
data: [],
total: 0,
isPending: false,
} as any
}
>
<DataTableBase
empty={<div>Empty Component</div>}
loading={<div>Loading</div>}
hasBulkActions={false}
>
<div>Table Header and Body</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Empty Component')).not.toBeNull();
expect(screen.queryByText('Table Header and Body')).toBeNull();
});
Comment on lines +88 to +114
Copy link
Contributor

Choose a reason for hiding this comment

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

This test looks duplicate of test at line 46. If confirmed can you remove it?


it('should display the current data when data is refreshing', () => {
render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={
{
...defaultListContext,
data: [{ id: 1 }],
total: 1,
isPending: false,
isFetching: true,
} as any
}
>
<DataTableBase
empty={<div>Empty</div>}
loading={<div>Loading Component</div>}
hasBulkActions={false}
>
<div>Child Content</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Child Content')).not.toBeNull();
expect(screen.queryByText('Loading Component')).toBeNull();
});

it('should provide ResourceContext to the loading component', () => {
const Loading = () => {
const resource = useResourceContext();
return <div>Loading resource: {resource}</div>;
};

render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={{ ...defaultListContext, isPending: true } as any}
>
<DataTableBase
empty={<div>Empty</div>}
loading={<Loading />}
hasBulkActions={false}
>
<div>Child</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Loading resource: posts')).not.toBeNull();
});

it('should provide ResourceContext to the empty component', () => {
const Empty = () => {
const resource = useResourceContext();
return <div>Empty resource: {resource}</div>;
};

render(
<ResourceContextProvider value="posts">
<ListContextProvider
value={
{
...defaultListContext,
isPending: false,
data: [],
} as any
}
>
<DataTableBase
empty={<Empty />}
loading={<div>Loading</div>}
hasBulkActions={false}
>
<div>Child</div>
</DataTableBase>
</ListContextProvider>
</ResourceContextProvider>
);

expect(screen.queryByText('Empty resource: posts')).not.toBeNull();
});
});
70 changes: 35 additions & 35 deletions packages/ra-core/src/dataTable/DataTableBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,43 +145,43 @@ export const DataTableBase = function DataTable<
]
);

if (isPending === true) {
return loading;
}

/**
* Once loaded, the data for the list may be empty. Instead of
* displaying the table header with zero data rows,
* the DataTable displays the empty component.
*/
if (data == null || data.length === 0 || total === 0) {
return empty ?? null;
}

/**
* After the initial load, if the data for the list isn't empty,
* and even if the data is refreshing (e.g. after a filter change),
* the DataTable displays the current data.
*/
return (
<DataTableStoreContext.Provider value={storeContextValue}>
<DataTableSortContext.Provider value={sort}>
<DataTableSelectedIdsContext.Provider value={selectedIds}>
<DataTableCallbacksContext.Provider
value={callbacksContextValue}
>
<DataTableConfigContext.Provider
value={configContextValue}
>
<OptionalResourceContextProvider value={resource}>
<DataTableDataContext.Provider value={data}>
{children}
</DataTableDataContext.Provider>
</OptionalResourceContextProvider>
</DataTableConfigContext.Provider>
</DataTableCallbacksContext.Provider>
</DataTableSelectedIdsContext.Provider>
</DataTableSortContext.Provider>
<DataTableConfigContext.Provider value={configContextValue}>
<OptionalResourceContextProvider value={resource}>
{isPending ? (
loading
) : data == null || data.length === 0 || total === 0 ? (
/**
* Once loaded, the data for the list may be empty.
* Instead of displaying the table header
* with zero data rows, the DataTable
* displays the empty component.
*/
empty
) : (
/**
* After the initial load, if the data for the list
* isn't empty, and even if the data is refreshing
* (e.g. after a filter change), the DataTable
* displays the current data.
*/
<DataTableSortContext.Provider value={sort}>
<DataTableCallbacksContext.Provider
value={callbacksContextValue}
>
<DataTableSelectedIdsContext.Provider
value={selectedIds}
>
<DataTableDataContext.Provider value={data}>
{children}
</DataTableDataContext.Provider>
</DataTableSelectedIdsContext.Provider>
</DataTableCallbacksContext.Provider>
</DataTableSortContext.Provider>
)}
</OptionalResourceContextProvider>
</DataTableConfigContext.Provider>
</DataTableStoreContext.Provider>
);
};
Expand Down
Loading