Skip to content

Commit 836fffc

Browse files
committed
Add <ReferenceManyField empty> prop
1 parent a450382 commit 836fffc

File tree

3 files changed

+188
-58
lines changed

3 files changed

+188
-58
lines changed

docs/ReferenceManyField.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ This example leverages [`<SingleFieldList>`](./SingleFieldList.md) to display an
9090
| -------------- | -------- | --------------------------------------------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------- |
9191
| `children` | Required | `Element` | - | One or several elements that render a list of records based on a `ListContext` |
9292
| `debounce` | Optional | `number` | 500 | debounce time in ms for the `setFilters` callbacks |
93+
| `empty` | Optional | `Element` | - | Element to display when there are no related records. |
9394
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records, passed to `getManyReference()` |
9495
| `pagination` | Optional | `Element` | - | Pagination element to display pagination controls. empty by default (no pagination) |
9596
| `perPage` | Optional | `number` | 25 | Maximum number of referenced records to fetch |
@@ -176,6 +177,44 @@ const PostCommentsField = () => (
176177
);
177178
```
178179

180+
## `empty`
181+
182+
Use `empty` to customize the text displayed when the related record is empty.
183+
184+
```jsx
185+
<ReferenceManyField
186+
reference="books"
187+
target="author_id"
188+
empty="no books"
189+
>
190+
...
191+
</ReferenceManyField>
192+
```
193+
194+
`empty` also accepts a translation key.
195+
196+
```jsx
197+
<ReferenceManyField
198+
reference="books"
199+
target="author_id"
200+
empty="resources.authors.fields.books.empty"
201+
>
202+
...
203+
</ReferenceManyField>
204+
```
205+
206+
`empty` also accepts a `ReactElement`.
207+
208+
```jsx
209+
<ReferenceManyField
210+
reference="books"
211+
target="author_id"
212+
empty={<CreateButton resource="books" />}
213+
>
214+
...
215+
</ReferenceManyField>
216+
```
217+
179218
## `filter`: Permanent Filter
180219

181220
You can filter the query used to populate the possible values. Use the `filter` prop for that.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React, { ReactElement, ReactNode } from 'react';
2+
import { ResourceContextProvider } from '../../core';
3+
import { ListContextProvider } from '../list/ListContextProvider';
4+
import {
5+
useReferenceManyFieldController,
6+
type UseReferenceManyFieldControllerParams,
7+
} from './useReferenceManyFieldController';
8+
import type { RaRecord } from '../../types';
9+
10+
/**
11+
* Render related records to the current one.
12+
*
13+
* You must define the fields to be passed to the iterator component as children.
14+
*
15+
* @example Display all the comments of the current post as a datagrid
16+
* <ReferenceManyFieldBase reference="comments" target="post_id">
17+
* <Datagrid>
18+
* <TextField source="id" />
19+
* <TextField source="body" />
20+
* <DateField source="created_at" />
21+
* <EditButton />
22+
* </Datagrid>
23+
* </ReferenceManyFieldBase>
24+
*
25+
* @example Display all the books by the current author, only the title
26+
* <ReferenceManyFieldBase reference="books" target="author_id">
27+
* <SingleFieldList>
28+
* <ChipField source="title" />
29+
* </SingleFieldList>
30+
* </ReferenceManyFieldBase>
31+
*
32+
* By default, restricts the displayed values to 25. You can extend this limit
33+
* by setting the `perPage` prop.
34+
*
35+
* @example
36+
* <ReferenceManyFieldBase perPage={10} reference="comments" target="post_id">
37+
* ...
38+
* </ReferenceManyFieldBase>
39+
*
40+
* By default, orders the possible values by id desc. You can change this order
41+
* by setting the `sort` prop (an object with `field` and `order` properties).
42+
*
43+
* @example
44+
* <ReferenceManyFieldBase sort={{ field: 'created_at', order: 'DESC' }} reference="comments" target="post_id">
45+
* ...
46+
* </ReferenceManyFieldBase>
47+
*
48+
* Also, you can filter the query used to populate the possible values. Use the
49+
* `filter` prop for that.
50+
*
51+
* @example
52+
* <ReferenceManyFieldBase filter={{ is_published: true }} reference="comments" target="post_id">
53+
* ...
54+
* </ReferenceManyFieldBase>
55+
*/
56+
export const ReferenceManyFieldBase = <
57+
RecordType extends RaRecord = RaRecord,
58+
ReferenceRecordType extends RaRecord = RaRecord,
59+
>(
60+
props: ReferenceManyFieldBaseProps<RecordType, ReferenceRecordType>
61+
) => {
62+
const {
63+
children,
64+
debounce,
65+
empty,
66+
filter = defaultFilter,
67+
page = 1,
68+
pagination = null,
69+
perPage = 25,
70+
record,
71+
reference,
72+
resource,
73+
sort = defaultSort,
74+
source = 'id',
75+
storeKey,
76+
target,
77+
queryOptions,
78+
} = props;
79+
80+
const controllerProps = useReferenceManyFieldController<
81+
RecordType,
82+
ReferenceRecordType
83+
>({
84+
debounce,
85+
filter,
86+
page,
87+
perPage,
88+
record,
89+
reference,
90+
resource,
91+
sort,
92+
source,
93+
storeKey,
94+
target,
95+
queryOptions,
96+
});
97+
98+
if (
99+
// there is an empty page component
100+
empty &&
101+
// there is no error
102+
!controllerProps.error &&
103+
// the list is not loading data for the first time
104+
!controllerProps.isPending &&
105+
// the API returned no data (using either normal or partial pagination)
106+
(controllerProps.total === 0 ||
107+
(controllerProps.total == null &&
108+
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
109+
controllerProps.hasPreviousPage === false &&
110+
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
111+
controllerProps.hasNextPage === false &&
112+
// @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it
113+
controllerProps.data.length === 0)) &&
114+
// the user didn't set any filters
115+
!Object.keys(controllerProps.filterValues).length
116+
) {
117+
return empty;
118+
}
119+
120+
return (
121+
<ResourceContextProvider value={reference}>
122+
<ListContextProvider value={controllerProps}>
123+
{children}
124+
{pagination}
125+
</ListContextProvider>
126+
</ResourceContextProvider>
127+
);
128+
};
129+
130+
export interface ReferenceManyFieldBaseProps<
131+
RecordType extends Record<string, any> = Record<string, any>,
132+
ReferenceRecordType extends Record<string, any> = Record<string, any>,
133+
> extends UseReferenceManyFieldControllerParams<
134+
RecordType,
135+
ReferenceRecordType
136+
> {
137+
children: ReactNode;
138+
empty?: ReactNode;
139+
pagination?: ReactElement;
140+
}
141+
142+
const defaultFilter = {};
143+
const defaultSort = { field: 'id', order: 'DESC' as const };
Lines changed: 6 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import React, { ReactElement, ReactNode } from 'react';
1+
import React from 'react';
22
import {
3-
useReferenceManyFieldController,
4-
ListContextProvider,
5-
ResourceContextProvider,
3+
ReferenceManyFieldBase,
4+
type ReferenceManyFieldBaseProps,
65
RaRecord,
7-
UseReferenceManyFieldControllerParams,
86
} from 'ra-core';
97

10-
import { FieldProps } from './types';
8+
import type { FieldProps } from './types';
119

1210
/**
1311
* Render related records to the current one.
@@ -60,60 +58,10 @@ export const ReferenceManyField = <
6058
ReferenceRecordType extends RaRecord = RaRecord,
6159
>(
6260
props: ReferenceManyFieldProps<RecordType, ReferenceRecordType>
63-
) => {
64-
const {
65-
children,
66-
debounce,
67-
filter = defaultFilter,
68-
page = 1,
69-
pagination = null,
70-
perPage = 25,
71-
record,
72-
reference,
73-
resource,
74-
sort = defaultSort,
75-
source = 'id',
76-
storeKey,
77-
target,
78-
queryOptions,
79-
} = props;
80-
81-
const controllerProps = useReferenceManyFieldController<
82-
RecordType,
83-
ReferenceRecordType
84-
>({
85-
debounce,
86-
filter,
87-
page,
88-
perPage,
89-
record,
90-
reference,
91-
resource,
92-
sort,
93-
source,
94-
storeKey,
95-
target,
96-
queryOptions,
97-
});
98-
99-
return (
100-
<ResourceContextProvider value={reference}>
101-
<ListContextProvider value={controllerProps}>
102-
{children}
103-
{pagination}
104-
</ListContextProvider>
105-
</ResourceContextProvider>
106-
);
107-
};
61+
) => <ReferenceManyFieldBase<RecordType, ReferenceRecordType> {...props} />;
10862

10963
export interface ReferenceManyFieldProps<
11064
RecordType extends Record<string, any> = Record<string, any>,
11165
ReferenceRecordType extends Record<string, any> = Record<string, any>,
11266
> extends Omit<FieldProps<RecordType>, 'source'>,
113-
UseReferenceManyFieldControllerParams<RecordType, ReferenceRecordType> {
114-
children: ReactNode;
115-
pagination?: ReactElement;
116-
}
117-
118-
const defaultFilter = {};
119-
const defaultSort = { field: 'id', order: 'DESC' as const };
67+
ReferenceManyFieldBaseProps<RecordType, ReferenceRecordType> {}

0 commit comments

Comments
 (0)