Skip to content

Commit 3b98691

Browse files
committed
add render prop on ReferenceArrayField (remove pagination from ReferenceArrayFieldBase)
1 parent d72afc3 commit 3b98691

File tree

8 files changed

+100
-109
lines changed

8 files changed

+100
-109
lines changed

docs/ReferenceArrayField.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ You can change how the list of related records is rendered by passing a custom c
8686
| `source` | Required | `string` | - | Name of the property to display |
8787
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'tags' |
8888
| `children` | Optional | `Element` | `<SingleFieldList>` | One or several elements that render a list of records based on a `ListContext` |
89+
| `render` | Optional | `(listContext) => Element` | `<SingleFieldList>` | A function that takes a list context and render a list of records |
8990
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records (the filtering is done client-side) |
9091
| `pagination` | Optional | `Element` | - | Pagination element to display pagination controls. empty by default (no pagination) |
9192
| `perPage` | Optional | `number` | 1000 | Maximum number of results to display |
@@ -179,6 +180,38 @@ export const PostShow = () => (
179180
);
180181
```
181182

183+
184+
## `render`
185+
186+
Alternatively to children you can pass a render prop to `<ReferenceArrayField>`. The render prop will receive the list context as its argument, allowing to inline the render logic .
187+
When receiving a render prop the `<ReferenceArrayField>` component will ignore the children property.
188+
189+
190+
```jsx
191+
<ReferenceArrayField
192+
label="Tags"
193+
reference="tags"
194+
source="tag_ids"
195+
render={(context) => {
196+
197+
if (context.isPending) {
198+
return <p>Loading...</p>;
199+
}
200+
201+
if (context.error) {
202+
return <p className="error">{context.error.toString()}</p>;
203+
}
204+
return (
205+
<p>
206+
{listContext.data?.map((tag, index) => (
207+
<li key={index}>{tag.name}</li>
208+
))}
209+
</p>
210+
);
211+
}}
212+
/>
213+
```
214+
182215
## `filter`
183216
184217
`<ReferenceArrayField>` fetches all the related records, and displays them all, too. You can use the `filter` prop to filter the list of related records to display (this works by filtering the records client-side, after the fetch).

docs/ReferenceArrayFieldBase.md

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ You can change how the list of related records is rendered by passing a custom c
9898
| `children` | Required if no render | `Element` | | One or several elements that render a list of records based on a `ListContext` |
9999
| `render` | Required if no children | `(listContext) => Element` | | A function that takes a list context and render a list of records |
100100
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records (the filtering is done client-side) |
101-
| `pagination` | Optional | `Element` | - | Pagination element to display pagination controls. empty by default (no pagination) |
102101
| `perPage` | Optional | `number` | 1000 | Maximum number of results to display |
103102
| `queryOptions` | Optional | [`UseQuery Options`](https://tanstack.com/query/v5/docs/react/reference/useQuery) | `{}` | `react-query` options for the `getMany` query |
104103
| `sort` | Optional | `{ field, order }` | `{ field: 'id', order: 'DESC' }` | Sort order to use when displaying the related records (the sort is done client-side) |
@@ -139,8 +138,8 @@ const MyPostView = (props: { children: React.ReactNode }) => {
139138
140139
## `render`
141140
142-
Alternatively to children you can pass a render prop to `<ReferenceArrayFieldBase>`. The render prop will receive the list context as its argument, allowing to inline the render logic for both the list and the pagination.
143-
When receiving a render prop the `<ReferenceArrayFieldBase>` component will ignore the children and the pagination property.
141+
Alternatively to children you can pass a render prop to `<ReferenceArrayFieldBase>`. The render prop will receive the list context as its argument, allowing to inline the render logic .
142+
When receiving a render prop the `<ReferenceArrayFieldBase>` component will ignore the children property.
144143
145144
146145
```jsx
@@ -185,26 +184,6 @@ For instance, to render only tags that are 'published', you can use the followin
185184
```
186185
{% endraw %}
187186
188-
## `pagination`
189-
190-
`<ReferenceArrayFieldBase>` fetches *all* the related fields, and puts them all in a `ListContext`. If a record has a large number of related records, you can limit the number of displayed records with the [`perPage`](#perpage) prop. Then, let users display remaining records by rendering pagination controls. For that purpose, pass a pagination element to the `pagination` prop.
191-
192-
For instance, to limit the display of related records to 10, you can use the following code:
193-
194-
```jsx
195-
import { Pagination, ReferenceArrayFieldBase } from 'react-admin';
196-
197-
<ReferenceArrayFieldBase
198-
label="Tags"
199-
source="tag_ids"
200-
reference="tags"
201-
perPage={10}
202-
pagination={<Pagination />}
203-
/>
204-
```
205-
206-
***Note:*** The pagination prop will be ignored when the component receive a render prop
207-
208187
## `perPage`
209188
210189
`<ReferenceArrayFieldBase>` fetches *all* the related fields, and puts them all in a `ListContext`. If a record has a large number of related records, it may be a good idea to limit the number of displayed records. The `perPage` prop allows to create a client-side pagination for the related records.
@@ -215,8 +194,6 @@ For instance, to limit the display of related records to 10, you can use the fol
215194
<ReferenceArrayFieldBase label="Tags" source="tag_ids" reference="tags" perPage={10} />
216195
```
217196
218-
If you want to let the user display the remaining records, you have to pass a [`pagination`](#pagination) element.
219-
220197
## `queryOptions`
221198
222199
Use the `queryOptions` prop to pass options to [the `dataProvider.getMany()` query](./useGetOne.md#aggregating-getone-calls) that fetches the referenced record.

packages/ra-core/src/controller/field/ReferenceArrayFieldBase.spec.tsx

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
Basic,
55
Errored,
66
Loading,
7-
WithPagination,
87
WithRenderProp,
98
} from './ReferenceArrayFieldBase.stories';
109

@@ -71,24 +70,6 @@ describe('ReferenceArrayFieldBase', () => {
7170
});
7271
});
7372

74-
it('should render pagination', async () => {
75-
render(<WithPagination />);
76-
await waitFor(() => {
77-
expect(screen.queryByText('1 - 3 of 8')).not.toBeNull();
78-
expect(screen.queryByText('Next Page')).not.toBeNull();
79-
expect(screen.queryByText('Previous Page')).not.toBeNull();
80-
});
81-
screen.getByText('Next Page').click();
82-
await waitFor(() => {
83-
expect(screen.queryByText('4 - 6 of 8')).not.toBeNull();
84-
});
85-
screen.getByText('Previous Page').click();
86-
87-
await waitFor(() => {
88-
expect(screen.queryByText('1 - 3 of 8')).not.toBeNull();
89-
});
90-
});
91-
9273
it('should support renderProp', async () => {
9374
render(<WithRenderProp />);
9475
await waitFor(() => {
@@ -102,12 +83,4 @@ describe('ReferenceArrayFieldBase', () => {
10283
expect(screen.queryByText('Charlie Watts')).not.toBeNull();
10384
});
10485
});
105-
106-
it('should not render pagination when given a render prop', async () => {
107-
render(<WithRenderProp pagination={<p>Custom Pagination</p>} />);
108-
await waitFor(() => {
109-
expect(screen.queryByText('John Lennon')).not.toBeNull();
110-
});
111-
expect(screen.queryByText('Custom Pagination')).toBeNull();
112-
});
11386
});

packages/ra-core/src/controller/field/ReferenceArrayFieldBase.stories.tsx

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -67,31 +67,6 @@ export const Basic = ({
6767
</TestMemoryRouter>
6868
);
6969

70-
export const WithPagination = () => (
71-
<TestMemoryRouter initialEntries={['/bands/1/show']}>
72-
<CoreAdmin dataProvider={defaultDataProvider}>
73-
<Resource name="artists" />
74-
<Resource
75-
name="bands"
76-
show={
77-
<ShowBase resource="bands" id={1}>
78-
<ReferenceArrayFieldBase
79-
source="members"
80-
reference="artists"
81-
pagination={<Pagination />}
82-
perPage={3}
83-
>
84-
<MyReferenceArrayField>
85-
<List source="name" />
86-
</MyReferenceArrayField>
87-
</ReferenceArrayFieldBase>
88-
</ShowBase>
89-
}
90-
/>
91-
</CoreAdmin>
92-
</TestMemoryRouter>
93-
);
94-
9570
const erroredDataProvider = {
9671
...defaultDataProvider,
9772
getMany: _resource => Promise.reject(new Error('Error')),
@@ -110,7 +85,6 @@ export const Loading = () => (
11085

11186
export const WithRenderProp = ({
11287
dataProvider = defaultDataProvider,
113-
pagination,
11488
}: {
11589
dataProvider?: DataProvider;
11690
pagination?: React.ReactElement;
@@ -136,7 +110,6 @@ export const WithRenderProp = ({
136110
<ReferenceArrayFieldBase
137111
source="members"
138112
reference="artists"
139-
pagination={pagination}
140113
render={({ data, isPending, error }) => {
141114
if (isPending) {
142115
return <p>Loading...</p>;
@@ -189,26 +162,3 @@ const List = ({ source }: { source: string }) => {
189162
</p>
190163
);
191164
};
192-
193-
const Pagination = () => {
194-
const { page = 1, setPage, total = 0, perPage = 0 } = useListContext();
195-
const nextPage = () => {
196-
setPage?.(page + 1);
197-
};
198-
const previousPage = () => {
199-
setPage?.(page - 1);
200-
};
201-
return (
202-
<div>
203-
<button disabled={page <= 1} onClick={previousPage}>
204-
Previous Page
205-
</button>
206-
<span>
207-
{`${(page - 1) * perPage + 1} - ${Math.min(page * perPage, total)} of ${total}`}
208-
</span>
209-
<button disabled={page >= total / perPage} onClick={nextPage}>
210-
Next Page
211-
</button>
212-
</div>
213-
);
214-
};

packages/ra-core/src/controller/field/ReferenceArrayFieldBase.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export const ReferenceArrayFieldBase = <
7474
const {
7575
children,
7676
render,
77-
pagination,
7877
filter,
7978
page = 1,
8079
perPage,
@@ -110,7 +109,6 @@ export const ReferenceArrayFieldBase = <
110109
<ResourceContextProvider value={reference}>
111110
<ListContextProvider value={controllerProps}>
112111
{render ? render(controllerProps) : children}
113-
{!render && pagination}
114112
</ListContextProvider>
115113
</ResourceContextProvider>
116114
);
@@ -124,7 +122,6 @@ export interface ReferenceArrayFieldBaseProps<
124122
render?: (props: ListControllerResult<ReferenceRecordType>) => ReactElement;
125123
filter?: FilterPayload;
126124
page?: number;
127-
pagination?: ReactElement;
128125
perPage?: number;
129126
reference: string;
130127
sort?: SortPayload;

packages/ra-ui-materialui/src/field/ReferenceArrayField.spec.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { AdminContext } from '../AdminContext';
2828
import {
2929
DifferentIdTypes,
3030
WithPagination,
31+
WithRenderProp,
3132
} from './ReferenceArrayField.stories';
3233

3334
const theme = createTheme({});
@@ -333,6 +334,20 @@ describe('<ReferenceArrayField />', () => {
333334
expect(await screen.findByText('artist_3')).not.toBeNull();
334335
});
335336

337+
it('should support renderProp', async () => {
338+
render(<WithRenderProp />);
339+
await waitFor(() => {
340+
expect(screen.queryByText('John Lennon')).not.toBeNull();
341+
expect(screen.queryByText('Paul McCartney')).not.toBeNull();
342+
expect(screen.queryByText('Ringo Star')).not.toBeNull();
343+
expect(screen.queryByText('George Harrison')).not.toBeNull();
344+
expect(screen.queryByText('Mick Jagger')).not.toBeNull();
345+
expect(screen.queryByText('Keith Richards')).not.toBeNull();
346+
expect(screen.queryByText('Ronnie Wood')).not.toBeNull();
347+
expect(screen.queryByText('Charlie Watts')).not.toBeNull();
348+
});
349+
});
350+
336351
describe('"Select all" button', () => {
337352
it('should be displayed if an item is selected', async () => {
338353
render(<WithPagination />);

packages/ra-ui-materialui/src/field/ReferenceArrayField.stories.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,40 @@ export const WithPagination = () => (
164164
</ResourceDefinitionContextProvider>
165165
</AdminContext>
166166
);
167+
168+
export const WithRenderProp = () => (
169+
<AdminContext dataProvider={dataProvider} defaultTheme="light">
170+
<ResourceDefinitionContextProvider definitions={resouceDefs}>
171+
<Show resource="bands" id={1} sx={{ width: 600 }}>
172+
<SimpleShowLayout>
173+
<TextField source="name" />
174+
<ReferenceArrayField
175+
source="members"
176+
reference="artists"
177+
render={({ data, isPending, error }) => {
178+
if (isPending) {
179+
return <p>Loading...</p>;
180+
}
181+
182+
if (error) {
183+
return (
184+
<p style={{ color: 'red' }}>
185+
{error.toString()}
186+
</p>
187+
);
188+
}
189+
190+
return (
191+
<p>
192+
{data?.map((datum, index) => (
193+
<li key={index}>{datum.name}</li>
194+
))}
195+
</p>
196+
);
197+
}}
198+
/>
199+
</SimpleShowLayout>
200+
</Show>
201+
</ResourceDefinitionContextProvider>
202+
</AdminContext>
203+
);

packages/ra-ui-materialui/src/field/ReferenceArrayField.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const ReferenceArrayField = <
8080
ReferenceRecordType extends RaRecord = RaRecord,
8181
>({
8282
pagination,
83+
render,
8384
...inProps
8485
}: ReferenceArrayFieldProps<RecordType, ReferenceRecordType>) => {
8586
const props = useThemeProps({
@@ -88,7 +89,11 @@ export const ReferenceArrayField = <
8889
});
8990
return (
9091
<ReferenceArrayFieldBase {...inProps}>
91-
<PureReferenceArrayFieldView {...props} pagination={pagination} />
92+
<PureReferenceArrayFieldView
93+
{...props}
94+
pagination={pagination}
95+
render={render}
96+
/>
9297
</ReferenceArrayFieldBase>
9398
);
9499
};
@@ -107,8 +112,10 @@ export interface ReferenceArrayFieldViewProps
107112
export const ReferenceArrayFieldView = (
108113
props: ReferenceArrayFieldViewProps
109114
) => {
110-
const { children, pagination, className, sx } = props;
111-
const { isPending, total } = useListContext();
115+
const { children, render, pagination, className, sx } = props;
116+
const listContext = useListContext();
117+
118+
const { isPending, total } = listContext;
112119

113120
return (
114121
<Root className={className} sx={sx}>
@@ -118,7 +125,9 @@ export const ReferenceArrayFieldView = (
118125
/>
119126
) : (
120127
<span>
121-
{children || <SingleFieldList />}
128+
{(render ? render(listContext) : children) || (
129+
<SingleFieldList />
130+
)}
122131
{pagination && total !== undefined ? pagination : null}
123132
</span>
124133
)}

0 commit comments

Comments
 (0)