Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add <ReferenceOneField queryOptions> support #8348

Merged
merged 1 commit into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions docs/ReferenceOneField.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,14 @@ const BookShow = () => (

## Properties

| Prop | Required | Type | Default | Description |
| ------------ | -------- | ------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------- |
| `children` | Optional | `Element` | - | The Field element used to render the referenced record |
| `link` | Optional | `string | Function` | `edit` | Target of the link wrapping the rendered child. Set to `false` to disable the link. |
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'book_details' |
| `target` | Required | string | - | Target field carrying the relationship on the referenced resource, e.g. 'book_id' |
| `sort` | Optional | `{ field: String, order: 'ASC' or 'DESC' }` | `{ field: 'id', order: 'ASC' }` | Used to order referenced records |
| `filter` | Optional | `Object` | `{}` | Used to filter referenced records |
| Prop | Required | Type | Default | Description |
| -------------- | -------- | ------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------- |
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'book_details' |
| `target` | Required | string | - | Target field carrying the relationship on the referenced resource, e.g. 'book_id' |
| `children` | Optional | `Element` | - | The Field element used to render the referenced record |
| `filter` | Optional | `Object` | `{}` | Used to filter referenced records |
| `link` | Optional | `string | Function` | `edit` | Target of the link wrapping the rendered child. Set to `false` to disable the link. |
| `queryOptions` | Optional | [`UseQueryOptions`](https://tanstack.com/query/v4/docs/reference/useQuery?from=reactQueryV3&original=https://react-query-v3.tanstack.com/reference/useQuery) | `{}` | `react-query` client options |
| `sort` | Optional | `{ field: String, order: 'ASC' or 'DESC' }` | `{ field: 'id', order: 'ASC' }` | Used to order referenced records |

`<ReferenceOneField>` also accepts the [common field props](./Fields.md#common-field-props), except `emptyText` (use the child `empty` prop instead).
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import get from 'lodash/get';
import { UseQueryOptions } from 'react-query';

import { useGetManyReference } from '../../dataProvider';
import { useNotify } from '../../notification';
import { RaRecord, SortPayload } from '../../types';
import { UseReferenceResult } from '../useReference';

export interface UseReferenceOneFieldControllerParams {
export interface UseReferenceOneFieldControllerParams<
RecordType extends RaRecord = any
> {
record?: RaRecord;
reference: string;
source?: string;
target: string;
sort?: SortPayload;
filter?: any;
queryOptions?: UseQueryOptions<{
data: RecordType[];
total: number;
WiXSL marked this conversation as resolved.
Show resolved Hide resolved
}> & { meta?: any };
}

/**
Expand All @@ -37,27 +44,34 @@ export interface UseReferenceOneFieldControllerParams {
* @prop {Object} props.filter The filter to apply to the referenced records
* @returns {UseReferenceResult} The request state. Destructure as { referenceRecord, isLoading, error }.
*/
export const useReferenceOneFieldController = (
props: UseReferenceOneFieldControllerParams
): UseReferenceResult => {
export const useReferenceOneFieldController = <
RecordType extends RaRecord = any
>(
props: UseReferenceOneFieldControllerParams<RecordType>
): UseReferenceResult<RecordType> => {
const {
reference,
record,
target,
source = 'id',
sort = { field: 'id', order: 'ASC' },
filter = {},
queryOptions = {},
} = props;
const notify = useNotify();
const { meta, ...otherQueryOptions } = queryOptions;

const { data, error, isFetching, isLoading, refetch } = useGetManyReference(
const { data, error, isFetching, isLoading, refetch } = useGetManyReference<
RecordType
>(
reference,
{
target,
id: get(record, source),
pagination: { page: 1, perPage: 1 },
sort,
filter,
meta,
},
{
enabled: !!record,
Expand All @@ -78,6 +92,7 @@ export const useReferenceOneFieldController = (
},
}
),
...otherQueryOptions,
}
);

Expand Down
31 changes: 28 additions & 3 deletions packages/ra-ui-materialui/src/field/ReferenceOneField.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as React from 'react';
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';

import {
RecordRepresentation,
Basic,
EmptyWithTranslate,
QueryOptions,
} from './ReferenceOneField.stories';

describe('ReferenceOneField', () => {
Expand All @@ -17,9 +18,33 @@ describe('ReferenceOneField', () => {
await screen.findByText('9780393966473');
});

it('should translate emptyText', () => {
it('should translate emptyText', async () => {
render(<EmptyWithTranslate />);

expect(screen.findByText('Not found')).not.toBeNull();
await screen.findByText('Not found');
});
it('should accept a queryOptions prop', async () => {
const dataProvider = {
getManyReference: jest.fn().mockImplementationOnce(() =>
Promise.resolve({
data: [{ id: 1, ISBN: '9780393966473', genre: 'novel' }],
total: 1,
})
),
};
render(<QueryOptions dataProvider={dataProvider} />);
await waitFor(() => {
expect(dataProvider.getManyReference).toHaveBeenCalledWith(
'book_details',
{
id: 1,
target: 'book_id',
sort: { field: 'id', order: 'ASC' },
pagination: { page: 1, perPage: 1 },
filter: {},
meta: { foo: 'bar' },
}
);
});
});
});
24 changes: 19 additions & 5 deletions packages/ra-ui-materialui/src/field/ReferenceOneField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,13 @@ const ListWrapper = ({ children }) => (
<ThemeProvider theme={createTheme()}>
<Wrapper>
<ListContextProvider
value={{
total: 1,
data: [{ id: 1, title: 'War and Peace' }],
sort: { field: 'title', order: 'ASC' },
}}
value={
{
total: 1,
data: [{ id: 1, title: 'War and Peace' }],
sort: { field: 'title', order: 'ASC' },
} as any
}
>
{children}
</ListContextProvider>
Expand Down Expand Up @@ -310,3 +312,15 @@ export const RecordRepresentation = () => (
</ResourceContextProvider>
</CoreAdminContext>
);

export const QueryOptions = ({ dataProvider = defaultDataProvider }) => (
<Wrapper dataProvider={dataProvider}>
<ReferenceOneField
reference="book_details"
target="book_id"
queryOptions={{ meta: { foo: 'bar' } }}
>
<TextField source="ISBN" />
</ReferenceOneField>
</Wrapper>
);
17 changes: 14 additions & 3 deletions packages/ra-ui-materialui/src/field/ReferenceOneField.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { ReactNode } from 'react';
import PropTypes from 'prop-types';
import { UseQueryOptions } from 'react-query';
import { Typography } from '@mui/material';
import {
useReferenceOneFieldController,
Expand All @@ -9,6 +10,7 @@ import {
useCreatePath,
useTranslate,
SortPayload,
RaRecord,
} from 'ra-core';

import { PublicFieldProps, fieldPropTypes, InjectedFieldProps } from './types';
Expand All @@ -24,7 +26,9 @@ import { ReferenceFieldView } from './ReferenceField';
* <TextField source="body" />
* </ReferenceOneField>
*/
export const ReferenceOneField = (props: ReferenceOneFieldProps) => {
export const ReferenceOneField = <RecordType extends RaRecord = any>(
props: ReferenceOneFieldProps<RecordType>
) => {
const {
children,
reference,
Expand All @@ -34,6 +38,7 @@ export const ReferenceOneField = (props: ReferenceOneFieldProps) => {
sort,
filter,
link = false,
queryOptions,
} = props;
const record = useRecordContext(props);
const createPath = useCreatePath();
Expand All @@ -45,13 +50,14 @@ export const ReferenceOneField = (props: ReferenceOneFieldProps) => {
referenceRecord,
error,
refetch,
} = useReferenceOneFieldController({
} = useReferenceOneFieldController<RecordType>({
record,
reference,
source,
target,
sort,
filter,
queryOptions,
});

const resourceLinkPath =
Expand Down Expand Up @@ -89,7 +95,7 @@ export const ReferenceOneField = (props: ReferenceOneFieldProps) => {
);
};

export interface ReferenceOneFieldProps
export interface ReferenceOneFieldProps<RecordType extends RaRecord = any>
extends PublicFieldProps,
InjectedFieldProps {
children?: ReactNode;
Expand All @@ -98,6 +104,10 @@ export interface ReferenceOneFieldProps
sort?: SortPayload;
filter?: any;
link?: LinkToType;
queryOptions?: UseQueryOptions<{
data: RecordType[];
total: number;
WiXSL marked this conversation as resolved.
Show resolved Hide resolved
}> & { meta?: any };
}

ReferenceOneField.propTypes = {
Expand All @@ -108,6 +118,7 @@ ReferenceOneField.propTypes = {
reference: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
target: PropTypes.string.isRequired,
queryOptions: PropTypes.any,
};

ReferenceOneField.defaultProps = {
Expand Down