Skip to content

Commit 4ecac16

Browse files
committed
add render props to ReferenceFIeldBase component
1 parent 9462571 commit 4ecac16

File tree

3 files changed

+124
-8
lines changed

3 files changed

+124
-8
lines changed

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

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { CoreAdminContext } from '../../core/CoreAdminContext';
55
import { useResourceContext } from '../../core/useResourceContext';
66
import { testDataProvider } from '../../dataProvider';
77
import { ReferenceFieldBase } from './ReferenceFieldBase';
8-
import { Error, Loading, Meta } from './ReferenceFieldBase.stories';
8+
import {
9+
Errored,
10+
Loading,
11+
Meta,
12+
WithRenderProp,
13+
} from './ReferenceFieldBase.stories';
14+
import { WithRenderProps } from '../edit/EditBase.stories';
915

1016
describe('<ReferenceFieldBase />', () => {
1117
beforeAll(() => {
@@ -17,7 +23,7 @@ describe('<ReferenceFieldBase />', () => {
1723
.mockImplementationOnce(() => {})
1824
.mockImplementationOnce(() => {});
1925

20-
render(<Error />);
26+
render(<Errored />);
2127
await waitFor(() => {
2228
expect(screen.queryByText('Error')).not.toBeNull();
2329
});
@@ -40,8 +46,8 @@ describe('<ReferenceFieldBase />', () => {
4046
return <div>{resource}</div>;
4147
};
4248
const dataProvider = testDataProvider({
43-
// @ts-ignore
4449
getList: () =>
50+
// @ts-ignore
4551
Promise.resolve({ data: [{ id: 1 }, { id: 2 }], total: 2 }),
4652
});
4753
render(
@@ -65,6 +71,7 @@ describe('<ReferenceFieldBase />', () => {
6571
const dataProvider = testDataProvider({
6672
getMany,
6773
getOne: () =>
74+
// @ts-ignore
6875
Promise.resolve({
6976
data: {
7077
id: 1,
@@ -86,4 +93,62 @@ describe('<ReferenceFieldBase />', () => {
8693
});
8794
});
8895
});
96+
97+
describe('with render prop', () => {
98+
it('should display an error if error is defined', async () => {
99+
jest.spyOn(console, 'error')
100+
.mockImplementationOnce(() => {})
101+
.mockImplementationOnce(() => {});
102+
103+
const dataProviderWithAuthorsError = {
104+
getOne: () =>
105+
Promise.resolve({
106+
data: {
107+
id: 1,
108+
title: 'War and Peace',
109+
author: 1,
110+
summary:
111+
"War and Peace broadly focuses on Napoleon's invasion of Russia, and the impact it had on Tsarist society. The book explores themes such as revolution, revolution and empire, the growth and decline of various states and the impact it had on their economies, culture, and society.",
112+
year: 1869,
113+
},
114+
}),
115+
getMany: _resource => Promise.reject(new Error('Error')),
116+
} as any;
117+
118+
render(
119+
<WithRenderProp dataProvider={dataProviderWithAuthorsError} />
120+
);
121+
await waitFor(() => {
122+
expect(screen.queryByText('Error')).not.toBeNull();
123+
});
124+
});
125+
126+
it('should pass the loading state', async () => {
127+
jest.spyOn(console, 'error')
128+
.mockImplementationOnce(() => {})
129+
.mockImplementationOnce(() => {});
130+
131+
const dataProviderWithAuthorsLoading = {
132+
getOne: () =>
133+
Promise.resolve({
134+
data: {
135+
id: 1,
136+
title: 'War and Peace',
137+
author: 1,
138+
summary:
139+
"War and Peace broadly focuses on Napoleon's invasion of Russia, and the impact it had on Tsarist society. The book explores themes such as revolution, revolution and empire, the growth and decline of various states and the impact it had on their economies, culture, and society.",
140+
year: 1869,
141+
},
142+
}),
143+
getMany: _resource => new Promise(() => {}),
144+
} as any;
145+
146+
render(
147+
<WithRenderProp dataProvider={dataProviderWithAuthorsLoading} />
148+
);
149+
await waitFor(() => {
150+
expect(screen.queryByText('Loading...')).not.toBeNull();
151+
});
152+
});
153+
});
89154
});

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ const dataProviderWithAuthorsError = {
9494
year: 1869,
9595
},
9696
}),
97-
getMany: _resource => Promise.reject('Error'),
97+
getMany: _resource => Promise.reject(new Error('Error')),
9898
} as any;
9999

100-
export const Error = ({ dataProvider = dataProviderWithAuthorsError }) => (
100+
export const Errored = ({ dataProvider = dataProviderWithAuthorsError }) => (
101101
<TestMemoryRouter initialEntries={['/books/1/show']}>
102102
<CoreAdmin
103103
dataProvider={dataProvider}
@@ -351,6 +351,50 @@ export const Meta = ({
351351
</TestMemoryRouter>
352352
);
353353

354+
export const WithRenderProp = ({ dataProvider = dataProviderWithAuthors }) => (
355+
<TestMemoryRouter initialEntries={['/books/1/show']}>
356+
<CoreAdmin
357+
dataProvider={dataProvider}
358+
queryClient={
359+
new QueryClient({
360+
defaultOptions: {
361+
queries: {
362+
retry: false,
363+
},
364+
},
365+
})
366+
}
367+
>
368+
<Resource name="authors" />
369+
<Resource
370+
name="books"
371+
show={
372+
<ShowBase>
373+
<ReferenceFieldBase
374+
source="author"
375+
reference="authors"
376+
render={({ error, isPending }) => {
377+
if (isPending) {
378+
return <p>Loading...</p>;
379+
}
380+
381+
if (error) {
382+
return (
383+
<p style={{ color: 'red' }}>
384+
{error.message}
385+
</p>
386+
);
387+
}
388+
return <TextField source="first_name" />;
389+
}}
390+
/>
391+
</ShowBase>
392+
}
393+
/>
394+
</CoreAdmin>
395+
</TestMemoryRouter>
396+
);
397+
354398
const MyReferenceField = (props: { children: React.ReactNode }) => {
355399
const context = useReferenceFieldContext();
356400

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { ReactNode } from 'react';
33
import { UseQueryOptions } from '@tanstack/react-query';
44
import { ReferenceFieldContextProvider } from './ReferenceFieldContext';
55
import { RaRecord } from '../../types';
6-
import { useReferenceFieldController } from './useReferenceFieldController';
6+
import {
7+
useReferenceFieldController,
8+
UseReferenceFieldControllerResult,
9+
} from './useReferenceFieldController';
710
import { ResourceContextProvider } from '../../core';
811
import { RecordContextProvider } from '../record';
912
import { useFieldValue } from '../../util';
@@ -44,8 +47,9 @@ export const ReferenceFieldBase = <
4447
>(
4548
props: ReferenceFieldBaseProps<ReferenceRecordType>
4649
) => {
47-
const { children, empty = null } = props;
50+
const { children, render, empty = null } = props;
4851
const id = useFieldValue(props);
52+
4953
const controllerProps =
5054
useReferenceFieldController<ReferenceRecordType>(props);
5155

@@ -64,7 +68,7 @@ export const ReferenceFieldBase = <
6468
<ResourceContextProvider value={props.reference}>
6569
<ReferenceFieldContextProvider value={controllerProps}>
6670
<RecordContextProvider value={controllerProps.referenceRecord}>
67-
{children}
71+
{render ? render(controllerProps) : children}
6872
</RecordContextProvider>
6973
</ReferenceFieldContextProvider>
7074
</ResourceContextProvider>
@@ -75,6 +79,9 @@ export interface ReferenceFieldBaseProps<
7579
ReferenceRecordType extends RaRecord = RaRecord,
7680
> {
7781
children?: ReactNode;
82+
render?: (
83+
props: UseReferenceFieldControllerResult<ReferenceRecordType>
84+
) => ReactNode;
7885
className?: string;
7986
empty?: ReactNode;
8087
error?: ReactNode;

0 commit comments

Comments
 (0)