Skip to content

Commit ff912e8

Browse files
committed
add render prop on ReferenceArrayField (remove pagination from ReferenceArrayFieldBase)
1 parent 6f9dec5 commit ff912e8

File tree

5 files changed

+99
-4
lines changed

5 files changed

+99
-4
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ const MyTagList = (props: { children: React.ReactNode }) => {
142142
Alternatively to children you can pass a `render` function prop to `<ReferenceArrayFieldBase>`. The `render` prop will receive the `ListContext` as its argument, allowing to inline the `render` logic.
143143
When receiving a `render` prop the `<ReferenceArrayFieldBase>` component will ignore the children property.
144144
145+
145146
```jsx
146147
<ReferenceArrayFieldBase
147148
label="Tags"

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)