Skip to content

Commit 4e0ebea

Browse files
committed
create ReferenceArrayFieldBase and use it in MUI ReferenceArrayField
1 parent 04b3085 commit 4e0ebea

File tree

5 files changed

+253
-151
lines changed

5 files changed

+253
-151
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import * as React from 'react';
2+
import { type ReactElement, type ReactNode } from 'react';
3+
4+
import type { UseQueryOptions } from '@tanstack/react-query';
5+
import { FilterPayload, RaRecord, SortPayload } from '../../types';
6+
import { useRecordContext } from '../record';
7+
import { useReferenceArrayFieldController } from './useReferenceArrayFieldController';
8+
import { ResourceContextProvider } from '../../core';
9+
import { ListContextProvider } from '../list';
10+
import { FieldProps } from './types';
11+
12+
/**
13+
* A container component that fetches records from another resource specified
14+
* by an array of *ids* in current record.
15+
*
16+
* You must define the fields to be passed to the iterator component as children.
17+
*
18+
* @example Display all the products of the current order as datagrid
19+
* // order = {
20+
* // id: 123,
21+
* // product_ids: [456, 457, 458],
22+
* // }
23+
* <ReferenceArrayFieldBase label="Products" reference="products" source="product_ids">
24+
* <Datagrid>
25+
* <TextField source="id" />
26+
* <TextField source="description" />
27+
* <NumberField source="price" options={{ style: 'currency', currency: 'USD' }} />
28+
* <EditButton />
29+
* </Datagrid>
30+
* </ReferenceArrayFieldBase>
31+
*
32+
* @example Display all the categories of the current product as a list of chips
33+
* // product = {
34+
* // id: 456,
35+
* // category_ids: [11, 22, 33],
36+
* // }
37+
* <ReferenceArrayFieldBase label="Categories" reference="categories" source="category_ids">
38+
* <SingleFieldList>
39+
* <ChipField source="name" />
40+
* </SingleFieldList>
41+
* </ReferenceArrayFieldBase>
42+
*
43+
* By default, restricts the displayed values to 1000. You can extend this limit
44+
* by setting the `perPage` prop.
45+
*
46+
* @example
47+
* <ReferenceArrayFieldBase perPage={10} reference="categories" source="category_ids">
48+
* ...
49+
* </ReferenceArrayFieldBase>
50+
*
51+
* By default, the field displays the results in the order in which they are referenced
52+
* (i.e. in the order of the list of ids). You can change this order
53+
* by setting the `sort` prop (an object with `field` and `order` properties).
54+
*
55+
* @example
56+
* <ReferenceArrayFieldBase sort={{ field: 'name', order: 'ASC' }} reference="categories" source="category_ids">
57+
* ...
58+
* </ReferenceArrayFieldBase>
59+
*
60+
* Also, you can filter the results to display only a subset of values. Use the
61+
* `filter` prop for that.
62+
*
63+
* @example
64+
* <ReferenceArrayFieldBase filter={{ is_published: true }} reference="categories" source="category_ids">
65+
* ...
66+
* </ReferenceArrayFieldBase>
67+
*/
68+
export const ReferenceArrayFieldBase = <
69+
RecordType extends RaRecord = RaRecord,
70+
ReferenceRecordType extends RaRecord = RaRecord,
71+
>(
72+
props: ReferenceArrayFieldBaseProps<RecordType, ReferenceRecordType>
73+
) => {
74+
const {
75+
children,
76+
filter,
77+
page = 1,
78+
perPage,
79+
reference,
80+
resource,
81+
sort,
82+
source,
83+
queryOptions,
84+
} = props;
85+
const record = useRecordContext(props);
86+
const controllerProps = useReferenceArrayFieldController<
87+
RecordType,
88+
ReferenceRecordType
89+
>({
90+
filter,
91+
page,
92+
perPage,
93+
record,
94+
reference,
95+
resource,
96+
sort,
97+
source,
98+
queryOptions,
99+
});
100+
return (
101+
<ResourceContextProvider value={reference}>
102+
<ListContextProvider value={controllerProps}>
103+
{children}
104+
</ListContextProvider>
105+
</ResourceContextProvider>
106+
);
107+
};
108+
109+
export interface ReferenceArrayFieldBaseProps<
110+
RecordType extends RaRecord = RaRecord,
111+
ReferenceRecordType extends RaRecord = RaRecord,
112+
> extends FieldProps<RecordType> {
113+
children?: ReactNode;
114+
filter?: FilterPayload;
115+
page?: number;
116+
pagination?: ReactElement;
117+
perPage?: number;
118+
reference: string;
119+
sort?: SortPayload;
120+
queryOptions?: Omit<
121+
UseQueryOptions<ReferenceRecordType[], Error>,
122+
'queryFn' | 'queryKey'
123+
>;
124+
}

packages/ra-core/src/controller/field/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export * from './ReferenceFieldBase';
33
export * from './ReferenceFieldContext';
44
export * from './ReferenceManyCountBase';
55
export * from './ReferenceManyFieldBase';
6+
export * from './ReferenceArrayFieldBase';
7+
export * from './types';
68
export * from './useReferenceArrayFieldController';
79
export * from './useReferenceFieldController';
810
export * from './useReferenceManyFieldController';
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { ReactElement } from 'react';
2+
import { ExtractRecordPaths, HintedString } from '../../types';
3+
4+
type SortOrder = 'ASC' | 'DESC';
5+
6+
export interface FieldProps<
7+
RecordType extends Record<string, any> = Record<string, any>,
8+
> {
9+
/**
10+
* The field to use for sorting when users click this column head, if sortable.
11+
*
12+
* @see https://marmelab.com/react-admin/Fields.html#sortby
13+
* @example
14+
* const PostList = () => (
15+
* <List>
16+
* <Datagrid>
17+
* <TextField source="title" />
18+
* <ReferenceField source="author_id" sortBy="author.name">
19+
* <TextField source="name" />
20+
* </ReferenceField>
21+
* </Datagrid>
22+
* </List>
23+
* );
24+
*/
25+
sortBy?: HintedString<ExtractRecordPaths<RecordType>>;
26+
27+
/**
28+
* The order used for sorting when users click this column head, if sortable.
29+
*
30+
* @see https://marmelab.com/react-admin/Fields.html#sortbyorder
31+
* @example
32+
* const PostList = () => (
33+
* <List>
34+
* <Datagrid>
35+
* <TextField source="title" />
36+
* <DateField source="updated_at" sortByOrder="DESC" />
37+
* </Datagrid>
38+
* </List>
39+
* );
40+
*/
41+
sortByOrder?: SortOrder;
42+
43+
/**
44+
* Name of the property to display.
45+
*
46+
* @see https://marmelab.com/react-admin/Fields.html#source
47+
* @example
48+
* const CommentList = () => (
49+
* <List>
50+
* <Datagrid>
51+
* <TextField source="author.name" />
52+
* <TextField source="body" />
53+
* </Datagrid>
54+
* </List>
55+
* );
56+
*/
57+
source: ExtractRecordPaths<RecordType>;
58+
59+
/**
60+
* Label to use as column header when using <Datagrid> or <SimpleShowLayout>.
61+
* Defaults to the capitalized field name. Set to false to disable the label.
62+
*
63+
* @see https://marmelab.com/react-admin/Fields.html#label
64+
* @example
65+
* const PostList = () => (
66+
* <List>
67+
* <Datagrid>
68+
* <TextField source="title" />
69+
* <TextField source="body" label="Content" />
70+
* </Datagrid>
71+
* </List>
72+
* );
73+
*/
74+
label?: string | ReactElement | boolean;
75+
76+
/**
77+
* Set it to false to disable the click handler on the column header when used inside <Datagrid>.
78+
*
79+
* @see https://marmelab.com/react-admin/Fields.html#sortable
80+
* @example
81+
* const PostList = () => (
82+
* <List>
83+
* <Datagrid>
84+
* <TextField source="title" />
85+
* <ReferenceField source="author_id" sortable={false}>
86+
* <TextField source="name" />
87+
* </ReferenceField>
88+
* </Datagrid>
89+
* </List>
90+
* );
91+
*/
92+
sortable?: boolean;
93+
94+
/**
95+
* The text to display when the field value is empty. Defaults to empty string.
96+
*
97+
* @see https://marmelab.com/react-admin/Fields.html#emptytext
98+
* @example
99+
* const PostList = () => (
100+
* <List>
101+
* <Datagrid>
102+
* <TextField source="title" />
103+
* <TextField source="author" emptyText="missing data" />
104+
* </Datagrid>
105+
* </List>
106+
* );
107+
*/
108+
emptyText?: string;
109+
110+
/**
111+
* The current record to use. Defaults to the `RecordContext` value.
112+
*
113+
* @see https://marmelab.com/react-admin/Fields.html#record
114+
*/
115+
record?: RecordType;
116+
117+
/**
118+
* The resource name. Defaults to the `ResourceContext` value.
119+
*/
120+
resource?: string;
121+
}

packages/ra-ui-materialui/src/field/ReferenceArrayField.tsx

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import * as React from 'react';
22
import { memo, type ReactElement, type ReactNode } from 'react';
33
import {
4-
ListContextProvider,
54
useListContext,
65
type ListControllerProps,
7-
useReferenceArrayFieldController,
86
type SortPayload,
97
type FilterPayload,
10-
ResourceContextProvider,
11-
useRecordContext,
8+
ReferenceArrayFieldBase,
129
type RaRecord,
1310
} from 'ra-core';
1411
import {
@@ -90,37 +87,10 @@ export const ReferenceArrayField = <
9087
props: inProps,
9188
name: PREFIX,
9289
});
93-
const {
94-
filter,
95-
page = 1,
96-
perPage,
97-
reference,
98-
resource,
99-
sort,
100-
source,
101-
queryOptions,
102-
} = props;
103-
const record = useRecordContext(props);
104-
const controllerProps = useReferenceArrayFieldController<
105-
RecordType,
106-
ReferenceRecordType
107-
>({
108-
filter,
109-
page,
110-
perPage,
111-
record,
112-
reference,
113-
resource,
114-
sort,
115-
source,
116-
queryOptions,
117-
});
11890
return (
119-
<ResourceContextProvider value={reference}>
120-
<ListContextProvider value={controllerProps}>
121-
<PureReferenceArrayFieldView {...props} />
122-
</ListContextProvider>
123-
</ResourceContextProvider>
91+
<ReferenceArrayFieldBase {...inProps}>
92+
<PureReferenceArrayFieldView {...props} />
93+
</ReferenceArrayFieldBase>
12494
);
12595
};
12696
export interface ReferenceArrayFieldProps<

0 commit comments

Comments
 (0)