Skip to content

Commit

Permalink
Merge pull request #2575 from marmelab/customize-datagrid
Browse files Browse the repository at this point in the history
[RFR] Allow override of Datagrid Body and Row elements
  • Loading branch information
Gildas Garcia authored Nov 27, 2018
2 parents 005adc6 + f70f2e8 commit 69da6e2
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 52 deletions.
51 changes: 51 additions & 0 deletions docs/List.md
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ The datagrid component renders a list of records as a table. It is usually used
Here are all the props accepted by the component:
* [`body`](#body-element)
* [`rowStyle`](#row-style-function)
* [`rowClick`](#rowclick)
Expand All @@ -718,6 +719,56 @@ export const PostList = (props) => (
The datagrid is an *iterator* component: it receives an array of ids, and a data store, and is supposed to iterate over the ids to display each record. Another example of iterator component is [`<SingleFieldList>`](#the-singlefieldlist-component).
### Body element
By default, `<Datagrid>` renders its body using `<DatagridBody>`, an internal react-admin component. You can pass a custom component as the `row` prop to override that default. And by the way, `<DatagridBody>` has a `row` property set to `<DatagridRow>` by default for the same purpose. `<DatagridRow>` receives the row `record`, the `resource`, and a copy of the datagrid children. That means you can create a custom datagrid logic without copying several components from the react-admin source.
For instance, to show the selection checkbox only for records that have a `selectable` field set to true, you can override `<DatagridRow>` and `<DatagridBody>` as follows:
```jsx
// in src/PostList.js
import { Datagrid, DatagridBody, List, TextField } from 'react-admin';
import TableCell from '@material-ui/core/TableCell';
import TableRow from '@material-ui/core/TableRow';
import Checkbox from '@material-ui/core/Checkbox';

const MyDatagridRow = ({ record, resource, id, onToggleItem, children, selected, basePath }) => (
<TableRow key={id}>
{/* first column: selection checkbox */}
<TableCell padding="none">
{record.selectable && <Checkbox
checked={selected}
onClick={() => onToggleItem(id)}
/>}
</TableCell>
{/* data columns based on children */}
{React.Children.map(children, field => (
<TableCell key={`${id}-${field.props.source}`}>
{React.cloneElement(field, {
record,
basePath,
resource,
})}
</TableCell>
))}
</TableRow>
)

const MyDatagridBody = props => <DatagridBody {...props} row={<MyDatagridRow />} />;
const MyDatagrid = props => <Datagrid {...props} body={<MyDatagridBody />} />;

const PostList = props => (
<List {...props}>
<MyDatagrid>
<Textfield source="title" />
...
</MyDatagrid>
</List>
)

export default PostList;
```
### Row Style Function
You can customize the datagrid row style (applied to the `<tr>` element) based on the record, thanks to the `rowStyle` prop, which expects a function.
Expand Down
48 changes: 22 additions & 26 deletions packages/ra-ui-materialui/src/list/Datagrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,7 @@ const styles = {
* It is usually used as a child of the <List> and <ReferenceManyField> components.
*
* Props:
* - styles
* - rowStyle
* - options (passed as props to <Table>)
* - headerOptions (passed as props to mui <TableHead>)
* - bodyOptions (passed as props to mui <TableBody>)
* - rowOptions (passed as props to mui <TableRow>)
*
* @example Display all posts as a datagrid
* const postRowStyle = (record, index) => ({
Expand Down Expand Up @@ -100,22 +95,23 @@ class Datagrid extends Component {
render() {
const {
basePath,
data,
body,
children,
classes,
className,
currentSort,
data,
hasBulkActions,
hover,
ids,
isLoading,
onSelect,
onToggleItem,
resource,
rowClick,
rowStyle,
selectedIds,
setSort,
onSelect,
onToggleItem,
rowClick,
total,
version,
...rest
Expand Down Expand Up @@ -168,30 +164,29 @@ class Datagrid extends Component {
)}
</TableRow>
</TableHead>
<DatagridBody
basePath={basePath}
classes={classes}
rowClick={rowClick}
data={data}
hasBulkActions={hasBulkActions}
hover={hover}
ids={ids}
isLoading={isLoading}
onToggleItem={onToggleItem}
resource={resource}
rowStyle={rowStyle}
selectedIds={selectedIds}
version={version}
>
{children}
</DatagridBody>
{React.cloneElement(body, {
basePath,
classes,
rowClick,
data,
hasBulkActions,
hover,
ids,
isLoading,
onToggleItem,
resource,
rowStyle,
selectedIds,
version
}, children)}
</Table>
);
}
}

Datagrid.propTypes = {
basePath: PropTypes.string,
body: PropTypes.element.isRequired,
children: PropTypes.node.isRequired,
classes: PropTypes.object,
className: PropTypes.string,
Expand Down Expand Up @@ -220,6 +215,7 @@ Datagrid.defaultProps = {
hasBulkActions: false,
ids: [],
selectedIds: [],
body: <DatagridBody />
};

export default withStyles(styles)(Datagrid);
55 changes: 29 additions & 26 deletions packages/ra-ui-materialui/src/list/DatagridBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,40 @@ const DatagridBody = ({
isLoading,
onToggleItem,
resource,
row,
rowClick,
rowStyle,
selectedIds,
styles,
version,
...rest
}) => (
<TableBody className={classnames('datagrid-body', className)} {...rest}>
{ids.map((id, rowIndex) => (
<DatagridRow
basePath={basePath}
classes={classes}
className={classnames(classes.row, {
[classes.rowEven]: rowIndex % 2 === 0,
[classes.rowOdd]: rowIndex % 2 !== 0,
[classes.clickableRow]: rowClick,
})}
rowClick={rowClick}
hasBulkActions={hasBulkActions}
id={id}
key={id}
onToggleItem={onToggleItem}
record={data[id]}
resource={resource}
selected={selectedIds.includes(id)}
hover={hover}
style={rowStyle ? rowStyle(data[id], rowIndex) : null}
>
{children}
</DatagridRow>
))}
</TableBody>
);
<TableBody className={classnames('datagrid-body', className)} {...rest}>
{ids.map((id, rowIndex) => React.cloneElement(
row,
{
basePath,
classes,
className: classnames(classes.row, {
[classes.rowEven]: rowIndex % 2 === 0,
[classes.rowOdd]: rowIndex % 2 !== 0,
[classes.clickableRow]: rowClick,
}),
hasBulkActions,
hover,
id,
key: id,
onToggleItem,
record: data[id],
resource,
rowClick,
selected: selectedIds.includes(id),
style: rowStyle ? rowStyle(data[id], rowIndex) : null
},
children
))}
</TableBody>
);

DatagridBody.propTypes = {
basePath: PropTypes.string,
Expand All @@ -64,6 +65,7 @@ DatagridBody.propTypes = {
isLoading: PropTypes.bool,
onToggleItem: PropTypes.func,
resource: PropTypes.string,
row: PropTypes.element.isRequired,
rowClick: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
rowStyle: PropTypes.func,
selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired,
Expand All @@ -75,6 +77,7 @@ DatagridBody.defaultProps = {
data: {},
hasBulkActions: false,
ids: [],
row: <DatagridRow />
};

const areArraysEqual = (arr1, arr2) =>
Expand Down

0 comments on commit 69da6e2

Please sign in to comment.