Skip to content

Commit 4830383

Browse files
committed
add render props to ReferenceFIeldBase component
1 parent c886868 commit 4830383

File tree

3 files changed

+124
-9
lines changed

3 files changed

+124
-9
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
const defaultProps = {
@@ -23,7 +29,7 @@ describe('<ReferenceFieldBase />', () => {
2329
.mockImplementationOnce(() => {})
2430
.mockImplementationOnce(() => {});
2531

26-
render(<Error />);
32+
render(<Errored />);
2733
await waitFor(() => {
2834
expect(screen.queryByText('Error')).not.toBeNull();
2935
});
@@ -46,8 +52,8 @@ describe('<ReferenceFieldBase />', () => {
4652
return <div>{resource}</div>;
4753
};
4854
const dataProvider = testDataProvider({
49-
// @ts-ignore
5055
getList: () =>
56+
// @ts-ignore
5157
Promise.resolve({ data: [{ id: 1 }, { id: 2 }], total: 2 }),
5258
});
5359
render(
@@ -71,6 +77,7 @@ describe('<ReferenceFieldBase />', () => {
7177
const dataProvider = testDataProvider({
7278
getMany,
7379
getOne: () =>
80+
// @ts-ignore
7481
Promise.resolve({
7582
data: {
7683
id: 1,
@@ -92,4 +99,62 @@ describe('<ReferenceFieldBase />', () => {
9299
});
93100
});
94101
});
102+
103+
describe('with render prop', () => {
104+
it('should display an error if error is defined', async () => {
105+
jest.spyOn(console, 'error')
106+
.mockImplementationOnce(() => {})
107+
.mockImplementationOnce(() => {});
108+
109+
const dataProviderWithAuthorsError = {
110+
getOne: () =>
111+
Promise.resolve({
112+
data: {
113+
id: 1,
114+
title: 'War and Peace',
115+
author: 1,
116+
summary:
117+
"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.",
118+
year: 1869,
119+
},
120+
}),
121+
getMany: _resource => Promise.reject(new Error('Error')),
122+
} as any;
123+
124+
render(
125+
<WithRenderProp dataProvider={dataProviderWithAuthorsError} />
126+
);
127+
await waitFor(() => {
128+
expect(screen.queryByText('Error')).not.toBeNull();
129+
});
130+
});
131+
132+
it('should pass the loading state', async () => {
133+
jest.spyOn(console, 'error')
134+
.mockImplementationOnce(() => {})
135+
.mockImplementationOnce(() => {});
136+
137+
const dataProviderWithAuthorsLoading = {
138+
getOne: () =>
139+
Promise.resolve({
140+
data: {
141+
id: 1,
142+
title: 'War and Peace',
143+
author: 1,
144+
summary:
145+
"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.",
146+
year: 1869,
147+
},
148+
}),
149+
getMany: _resource => new Promise(() => {}),
150+
} as any;
151+
152+
render(
153+
<WithRenderProp dataProvider={dataProviderWithAuthorsLoading} />
154+
);
155+
await waitFor(() => {
156+
expect(screen.queryByText('Loading...')).not.toBeNull();
157+
});
158+
});
159+
});
95160
});

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

Lines changed: 47 additions & 3 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

@@ -359,7 +403,7 @@ const MyReferenceField = (props: { children: React.ReactNode }) => {
359403
}
360404

361405
if (context.error) {
362-
return <p style={{ color: 'red' }}>{context.error}</p>;
406+
return <p style={{ color: 'red' }}>{context.error.message}</p>;
363407
}
364408
return props.children;
365409
};

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

Lines changed: 9 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

@@ -43,7 +46,7 @@ export const ReferenceFieldBase = <
4346
>(
4447
props: ReferenceFieldBaseProps<ReferenceRecordType>
4548
) => {
46-
const { children } = props;
49+
const { children, render } = props;
4750

4851
const controllerProps =
4952
useReferenceFieldController<ReferenceRecordType>(props);
@@ -52,7 +55,7 @@ export const ReferenceFieldBase = <
5255
<ResourceContextProvider value={props.reference}>
5356
<ReferenceFieldContextProvider value={controllerProps}>
5457
<RecordContextProvider value={controllerProps.referenceRecord}>
55-
{children}
58+
{render ? render(controllerProps) : children}
5659
</RecordContextProvider>
5760
</ReferenceFieldContextProvider>
5861
</ResourceContextProvider>
@@ -63,6 +66,9 @@ export interface ReferenceFieldBaseProps<
6366
ReferenceRecordType extends RaRecord = RaRecord,
6467
> {
6568
children?: ReactNode;
69+
render?: (
70+
props: UseReferenceFieldControllerResult<ReferenceRecordType>
71+
) => ReactNode;
6672
className?: string;
6773
error?: ReactNode;
6874
queryOptions?: Partial<

0 commit comments

Comments
 (0)