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

<DatagridBody> creates the <RecordContext> instead of <DatagridRow> #9808

3 changes: 2 additions & 1 deletion docs/Upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -1206,8 +1206,9 @@ The deprecated `<ThemeProvider theme>` prop was removed. Use the `ThemesContext.

The `data-generator-retail` package has been updated to provide types for all its records. In the process, we renamed the `commands` resource to `orders`. Accordingly, the `nb_commands` property of the `customers` resource has been renamed to `nb_orders` and the `command_id` property of the `invoices` and `reviews` resources has been renamed to `order_id`.

## `<DatagridBody>` No Longer Provides `record` Prop To `<DatagridRow>`
adguernier marked this conversation as resolved.
Show resolved Hide resolved


The `<DatagridBody>` component no longer provides a `record` prop to its `<DatagridRow>` children. Instead, it provides a `recordContext` for each row. See the [`<Datagrid body/>`](./Datagrid.md#body) documentation to learn how to create your own row component.
adguernier marked this conversation as resolved.
Show resolved Hide resolved

## Upgrading to v4

Expand Down
56 changes: 30 additions & 26 deletions packages/ra-ui-materialui/src/list/datagrid/DatagridBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { cloneElement, memo, FC, ReactElement } from 'react';
import PropTypes from 'prop-types';
import { SxProps, TableBody, TableBodyProps } from '@mui/material';
import clsx from 'clsx';
import { Identifier, RaRecord } from 'ra-core';
import { Identifier, RaRecord, RecordContextProvider } from 'ra-core';

import { DatagridClasses } from './useDatagridStyles';
import DatagridRow, { PureDatagridRow, RowClickFunction } from './DatagridRow';
Expand Down Expand Up @@ -34,31 +34,35 @@ const DatagridBody: FC<DatagridBodyProps> = React.forwardRef(
className={clsx('datagrid-body', className, DatagridClasses.tbody)}
{...rest}
>
{data.map((record, rowIndex) =>
cloneElement(
row,
{
className: clsx(DatagridClasses.row, {
[DatagridClasses.rowEven]: rowIndex % 2 === 0,
[DatagridClasses.rowOdd]: rowIndex % 2 !== 0,
}),
expand,
hasBulkActions: hasBulkActions && !!selectedIds,
hover,
id: record.id ?? `row${rowIndex}`,
key: record.id ?? `row${rowIndex}`,
onToggleItem,
record,
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
resource,
rowClick,
selectable: !isRowSelectable || isRowSelectable(record),
selected: selectedIds?.includes(record.id),
sx: rowSx?.(record, rowIndex),
style: rowStyle?.(record, rowIndex),
},
children
)
)}
{data.map((record, rowIndex) => (
<RecordContextProvider
value={record}
key={record.id ?? `row${rowIndex}`}
>
{cloneElement(
row,
{
className: clsx(DatagridClasses.row, {
[DatagridClasses.rowEven]: rowIndex % 2 === 0,
[DatagridClasses.rowOdd]: rowIndex % 2 !== 0,
}),
expand,
hasBulkActions: hasBulkActions && !!selectedIds,
hover,
id: record.id ?? `row${rowIndex}`,
onToggleItem,
resource,
rowClick,
selectable:
!isRowSelectable || isRowSelectable(record),
selected: selectedIds?.includes(record.id),
sx: rowSx?.(record, rowIndex),
style: rowStyle?.(record, rowIndex),
},
children
)}
</RecordContextProvider>
))}
</TableBody>
)
);
Expand Down
170 changes: 103 additions & 67 deletions packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ResourceDefinitionContextProvider,
useRecordContext,
TestMemoryRouter,
RecordContextProvider,
} from 'ra-core';

import { AdminContext } from '../../AdminContext';
Expand Down Expand Up @@ -42,23 +43,26 @@ const render = element =>
describe('<DatagridRow />', () => {
const defaultProps = {
id: 15,
record: { id: 15, title: 'hello' },
resource: 'posts',
};

const defaultRecord = { id: 15, title: 'hello' };

describe('isRowExpandable', () => {
it('should show the expand button if it returns true', () => {
const contextValue = { isRowExpandable: () => true };

const { queryAllByText, getByText } = render(
<DatagridContextProvider value={contextValue}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</DatagridContextProvider>
);
expect(queryAllByText('expanded')).toHaveLength(0);
Expand All @@ -71,13 +75,15 @@ describe('<DatagridRow />', () => {

const { queryAllByText, getByText } = render(
<DatagridContextProvider value={contextValue}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</DatagridContextProvider>
);
expect(queryAllByText('expanded')).toHaveLength(0);
Expand All @@ -96,9 +102,11 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick="edit">
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick="edit">
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -111,9 +119,11 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick="show">
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick="show">
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -124,13 +134,15 @@ describe('<DatagridRow />', () => {

it("should change the expand state if the 'expand' option is selected", () => {
render(
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
expect(screen.queryAllByText('expanded')).toHaveLength(0);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -142,13 +154,15 @@ describe('<DatagridRow />', () => {
it("should execute the onToggleItem function if the 'toggleSelection' option is selected", () => {
const onToggleItem = jest.fn();
const { getByText } = render(
<DatagridRow
{...defaultProps}
onToggleItem={onToggleItem}
rowClick="toggleSelection"
>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
onToggleItem={onToggleItem}
rowClick="toggleSelection"
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
fireEvent.click(getByText('hello'));
expect(onToggleItem.mock.calls.length).toEqual(1);
Expand All @@ -157,14 +171,16 @@ describe('<DatagridRow />', () => {
it('should not execute the onToggleItem function if the row is not selectable', () => {
const onToggleItem = jest.fn();
const { getByText } = render(
<DatagridRow
{...defaultProps}
selectable={false}
onToggleItem={onToggleItem}
rowClick="toggleSelection"
>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
selectable={false}
onToggleItem={onToggleItem}
rowClick="toggleSelection"
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
fireEvent.click(getByText('hello'));
expect(onToggleItem).not.toHaveBeenCalled();
Expand All @@ -175,9 +191,11 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick={path}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick={path}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -191,9 +209,14 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick={customRowClick}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
rowClick={customRowClick}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -209,9 +232,11 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick={false}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick={false}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -224,9 +249,11 @@ describe('<DatagridRow />', () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<DatagridRow {...defaultProps} rowClick="">
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick="">
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
fireEvent.click(screen.getByText('hello'));
Expand All @@ -244,9 +271,11 @@ describe('<DatagridRow />', () => {
posts: { name: 'posts', hasEdit: true },
}}
>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</ResourceDefinitionContextProvider>
</LocationSpy>
);
Expand All @@ -265,9 +294,11 @@ describe('<DatagridRow />', () => {
posts: { name: 'posts', hasShow: true },
}}
>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</ResourceDefinitionContextProvider>
</LocationSpy>
);
Expand All @@ -290,9 +321,11 @@ describe('<DatagridRow />', () => {
},
}}
>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</ResourceDefinitionContextProvider>
</LocationSpy>
);
Expand All @@ -304,6 +337,7 @@ describe('<DatagridRow />', () => {

it('should default to false if the resource has no show nor edit page', () => {
let spy = jest.fn();
const { record, ...rest } = defaultProps;
render(
<LocationSpy spy={spy}>
<ResourceDefinitionContextProvider
Expand All @@ -314,9 +348,11 @@ describe('<DatagridRow />', () => {
},
}}
>
<DatagridRow {...defaultProps}>
<TitleField />
</DatagridRow>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...rest}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</ResourceDefinitionContextProvider>
</LocationSpy>
);
Expand Down
Loading
Loading