Skip to content

Commit

Permalink
Merge pull request #8192 from marmelab/add-queryoptions-ri
Browse files Browse the repository at this point in the history
Add `queryOptions` prop to `ReferenceInput`
  • Loading branch information
fzaninotto authored Sep 23, 2022
2 parents bd83464 + 86fae55 commit e083b46
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 23 deletions.
42 changes: 30 additions & 12 deletions docs/ReferenceInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ You can tweak how this component fetches the possible values using the `page`, `

## Props

| Prop | Required | Type | Default | Description |
|--------------------|----------|---------------------------------------------|----------------------------------|------------------------------------------------------------------------------------------|
| `source` | Required | `string` | - | Name of the entity property to use for the input value |
| `label` | Optional | `string` | - | Useful only when `ReferenceInput` is in a Filter array, the label is used as the Filter label.|
| `reference` | Required | `string` | '' | Name of the reference resource, e.g. 'posts'. |
| `children` | Optional | `ReactNode` | `<AutocompleteInput />` | The actual selection component |
| `filter` | Optional | `Object` | `{}` | Permanent filters to use for getting the suggestion list |
| `page` | Optional | `number` | 1 | The current page number |
| `perPage` | Optional | `number` | 25 | Number of suggestions to show |
| `sort` | Optional | `{ field: String, order: 'ASC' or 'DESC' }` | `{ field: 'id', order: 'DESC' }` | How to order the list of suggestions |
| `enableGetChoices` | Optional | `({q: string}) => boolean` | `() => true` | Function taking the `filterValues` and returning a boolean to enable the `getList` call. |
| Prop | Required | Type | Default | Description |
|--------------------|----------|---------------------------------------------|----------------------------------|------------------------------------------------------------------------------------------------|
| `source` | Required | `string` | - | Name of the entity property to use for the input value |
| `label` | Optional | `string` | - | Useful only when `ReferenceInput` is in a Filter array, the label is used as the Filter label. |
| `reference` | Required | `string` | '' | Name of the reference resource, e.g. 'posts'. |
| `children` | Optional | `ReactNode` | `<AutocompleteInput />` | The actual selection component |
| `filter` | Optional | `Object` | `{}` | Permanent filters to use for getting the suggestion list |
| `page` | Optional | `number` | 1 | The current page number |
| `perPage` | Optional | `number` | 25 | Number of suggestions to show |
| `sort` | Optional | `{ field: String, order: 'ASC' or 'DESC' }` | `{ field: 'id', order: 'DESC' }` | How to order the list of suggestions |
| `enableGetChoices` | Optional | `({q: string}) => boolean` | `() => true` | Function taking the `filterValues` and returning a boolean to enable the `getList` call. |
| `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 |

**Note**: `<ReferenceInput>` doesn't accept the [common input props](./Inputs.md#common-input-props) (like `label`) ; it is the responsibility of the child component to apply them.

Expand Down Expand Up @@ -79,7 +80,8 @@ You can make the `getList()` call lazy by using the `enableGetChoices` prop. Thi
<ReferenceInput
source="post_id"
reference="posts"
enableGetChoices={({ q }) => q.length >= 2} />
enableGetChoices={({ q }) => q.length >= 2}
/>
```

## `filter`
Expand Down Expand Up @@ -166,6 +168,22 @@ Then to display a selector for the post author, you should call `<ReferenceInput
<ReferenceInput source="author_id" reference="authors" />
```

## queryOptions

Use [the `queryOptions` prop](#queryoptions) to pass [a custom `meta`](./Actions.md#meta-parameter) to the `dataProvider.getList()` call.

```jsx
import { ReferenceInput, AutocompleteInput } from 'react-admin';

<ReferenceInput
source="post_id"
reference="posts"
queryOptions={{ meta: { foo: 'bar' } }}
>
<AutocompleteInput label="Post" />
</ReferenceInput>
```

## Performance

Why does `<ReferenceInput>` use the `dataProvider.getMany()` method with a single value `[id]` instead of `dataProvider.getOne()` to fetch the record for the current value?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const useReferenceInputController = <RecordType extends RaRecord = any>(
reference,
source,
} = props;
const { meta, ...otherQueryOptions } = queryOptions;

const [params, paramsModifiers] = useReferenceParams({
resource: reference,
Expand Down Expand Up @@ -94,11 +95,12 @@ export const useReferenceInputController = <RecordType extends RaRecord = any>(
},
sort: { field: params.sort, order: params.order },
filter: { ...params.filter, ...filter },
meta,
},
{
enabled: isGetMatchingEnabled,
keepPreviousData: true,
...queryOptions,
...otherQueryOptions,
}
);

Expand Down Expand Up @@ -186,7 +188,7 @@ export interface UseReferenceInputControllerParams<
hasNextPage?: boolean;
hasPreviousPage?: boolean;
};
}>;
}> & { meta?: any };
page?: number;
perPage?: number;
record?: RaRecord;
Expand Down
34 changes: 33 additions & 1 deletion packages/ra-ui-materialui/src/input/ReferenceInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient } from 'react-query';
import { testDataProvider, useChoicesContext } from 'ra-core';
import {
testDataProvider,
useChoicesContext,
CoreAdminContext,
Form,
} from 'ra-core';

import { ReferenceInput } from './ReferenceInput';
import { AdminContext } from '../AdminContext';
Expand Down Expand Up @@ -96,4 +101,31 @@ describe('<ReferenceInput />', () => {
expect(screen.getByLabelText('total').innerHTML).toEqual('2');
});
});

it('should accept meta in queryOptions', async () => {
const getList = jest
.fn()
.mockImplementationOnce(() =>
Promise.resolve({ data: [], total: 25 })
);
const dataProvider = testDataProvider({ getList });
render(
<CoreAdminContext dataProvider={dataProvider}>
<Form>
<ReferenceInput
{...defaultProps}
queryOptions={{ meta: { foo: 'bar' } }}
/>
</Form>
</CoreAdminContext>
);
await waitFor(() => {
expect(getList).toHaveBeenCalledWith('posts', {
filter: {},
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
meta: { foo: 'bar' },
});
});
});
});
12 changes: 4 additions & 8 deletions packages/ra-ui-materialui/src/input/ReferenceInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useReferenceInputController,
InputProps,
ResourceContextProvider,
UseReferenceInputControllerParams,
} from 'ra-core';

import { ReferenceError } from './ReferenceError';
Expand Down Expand Up @@ -118,15 +119,10 @@ ReferenceInput.defaultProps = {
children: <AutocompleteInput />,
};

export interface ReferenceInputProps extends InputProps {
export interface ReferenceInputProps
extends InputProps,
UseReferenceInputControllerParams {
children?: ReactElement;
label?: string;
page?: number;
perPage?: number;
reference: string;
// @deprecated
referenceSource?: (resource: string, source: string) => string;
resource?: string;
enableGetChoices?: (filters: any) => boolean;
[key: string]: any;
}

0 comments on commit e083b46

Please sign in to comment.