Skip to content

Commit cecbeed

Browse files
committed
Merge remote-tracking branch 'origin/next' into ColumnsButton-filter-input
2 parents 50f8e87 + d027119 commit cecbeed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3018
-199
lines changed

docs/CreateBase.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const BookCreate = () => (
4646
You can customize the `<CreateBase>` component using the following props, documented in the `<Create>` component:
4747

4848
* `children`: the components that renders the form
49+
* `render`: alternative to children, a function that takes the `CreateController` context and renders the form
4950
* [`disableAuthentication`](./Create.md#disableauthentication): disable the authentication check
5051
* [`mutationMode`](./Create.md#mutationmode): Switch to optimistic or undoable mutations (pessimistic by default)
5152
* [`mutationOptions`](./Create.md#mutationoptions): options for the `dataProvider.create()` call

docs/EditBase.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const BookEdit = () => (
4747
You can customize the `<EditBase>` component using the following props, documented in the `<Edit>` component:
4848

4949
* `children`: the components that renders the form
50+
* `render`: alternative to children, a function that takes the `EditController` context and renders the form
5051
* [`disableAuthentication`](./Edit.md#disableauthentication): disable the authentication check
5152
* [`id`](./Edit.md#id): the id of the record to edit
5253
* [`mutationMode`](./Edit.md#mutationmode): switch to optimistic or pessimistic mutations (undoable by default)

docs/ListBase.md

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,55 +6,81 @@ storybook_path: ra-core-controller-list-listbase--no-auth-provider
66

77
# `<ListBase>`
88

9-
`<ListBase>` is a headless variant of [`<List>`](./List.md). It fetches a list of records from the data provider, puts it in a [`ListContext`](./useListContext.md), and renders its children. Use it to build a custom list layout.
9+
`<ListBase>` is a headless List page component. It fetches a list of records from the data provider, puts it in a [`ListContext`](./useListContext.md), and renders its children. Use it to build a custom list layout.
1010

1111
Contrary to [`<List>`](./List.md), it does not render the page layout, so no title, no actions, no `<Card>`, and no pagination.
1212

1313
`<ListBase>` relies on the [`useListController`](./useListController.md) hook.
1414

1515
## Usage
1616

17-
You can use `ListBase` to create your own custom reusable List component, like this one:
17+
You can use `ListBase` to create your own custom List page component, like this one:
1818

1919
```jsx
2020
import {
21+
DataTable,
2122
ListBase,
22-
Title,
2323
ListToolbar,
24-
Pagination,
2524
DataTable,
25+
Pagination,
26+
Title,
2627
} from 'react-admin';
2728
import { Card } from '@mui/material';
2829

29-
const MyList = ({ children, actions, filters, title, ...props }) => (
30-
<ListBase {...props}>
31-
<Title title={title}/>
30+
const PostList = () => (
31+
<ListBase>
32+
<Title title="Post List"/>
3233
<ListToolbar
33-
filters={filters}
34-
actions={actions}
34+
filters={[
35+
{ source: 'q', label: 'Search', alwaysOn: true },
36+
{ source: 'published', label: 'Published', type: 'boolean' },
37+
]}
3538
/>
3639
<Card>
37-
{children}
40+
<DataTable>
41+
<DataTable.Col source="title" />
42+
<DataTable.Col source="author" />
43+
<DataTable.Col source="published_at" />
44+
</DataTable>
3845
</Card>
3946
<Pagination />
4047
</ListBase>
4148
);
49+
```
50+
51+
Alternatively, you can pass a `render` function prop instead of `children`. This function will receive the `ListContext` as argument.
4252

53+
```jsx
4354
const PostList = () => (
44-
<MyList title="Post List">
45-
<DataTable>
46-
...
47-
</DataTable>
48-
</MyList>
55+
<ListBase render={({ data, total, isPending, error }) => (
56+
<Card>
57+
<Title title="Post List" />
58+
<ListToolbar
59+
filters={[
60+
{ source: 'q', label: 'Search', alwaysOn: true },
61+
{ source: 'published', label: 'Published', type: 'boolean' },
62+
]}
63+
/>
64+
<DataTable>
65+
{data?.map(record => (
66+
<DataTable.Row key={record.id}>
67+
<DataTable.Col source="title" record={record} />
68+
<DataTable.Col source="author" record={record} />
69+
<DataTable.Col source="published_at" record={record} />
70+
</DataTable.Row>
71+
))}
72+
</DataTable>
73+
<Pagination total={total} />
74+
</Card>
75+
)} />
4976
);
5077
```
5178
52-
This custom List component has no aside component - it's up to you to add it in pure React.
53-
5479
## Props
5580
56-
The `<ListBase>` component accepts the same props as [`useListController`](./useListController.md):
81+
The `<ListBase>` component accepts the following props:
5782
83+
* `children`
5884
* [`debounce`](./List.md#debounce)
5985
* [`disableAuthentication`](./List.md#disableauthentication)
6086
* [`disableSyncWithLocation`](./List.md#disablesyncwithlocation)
@@ -63,13 +89,13 @@ The `<ListBase>` component accepts the same props as [`useListController`](./use
6389
* [`filterDefaultValues`](./List.md#filterdefaultvalues)
6490
* [`perPage`](./List.md#perpage)
6591
* [`queryOptions`](./List.md#queryoptions)
92+
* `render`
6693
* [`resource`](./List.md#resource)
6794
* [`sort`](./List.md#sort)
6895
69-
These are a subset of the props accepted by `<List>` - only the props that change data fetching, and not the props related to the user interface.
70-
7196
In addition, `<ListBase>` renders its children components inside a `ListContext`. Check [the `<List children>` documentation](./List.md#children) for usage examples.
7297
98+
7399
## Security
74100
75101
The `<ListBase>` component requires authentication and will redirect anonymous users to the login page. If you want to allow anonymous access, use the [`disableAuthentication`](./List.md#disableauthentication) prop.

docs/ReferenceArrayFieldBase.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
---
2+
layout: default
3+
title: "The ReferenceArrayFieldBase Component"
4+
storybook_path: ra-core-fields-referencearrayfieldbase--basic
5+
---
6+
7+
# `<ReferenceArrayFieldBase>`
8+
9+
Use `<ReferenceArrayFieldBase>` to display a list of related records, via a one-to-many relationship materialized by an array of foreign keys.
10+
11+
`<ReferenceArrayFieldBase>` fetches a list of referenced records (using the `dataProvider.getMany()` method), and puts them in a [`ListContext`](./useListContext.md). This component is headless, and its children need to use the data from this context to render the desired ui.
12+
13+
**Tip**: For a rendering a list of chips by default, use [the `<ReferenceArrayField>` component](./ReferenceArrayField.md) instead.
14+
15+
**Tip**: If the relationship is materialized by a foreign key on the referenced resource, use [the `<ReferenceManyFieldBase>` component](./ReferenceManyFieldBase.md) instead.
16+
17+
## Usage
18+
19+
For instance, let's consider a model where a `post` has many `tags`, materialized to a `tags_ids` field containing an array of ids:
20+
21+
```
22+
┌──────────────┐ ┌────────┐
23+
│ posts │ │ tags │
24+
│--------------│ │--------│
25+
│ id │ ┌───│ id │
26+
│ title │ │ │ name │
27+
│ body │ │ └────────┘
28+
│ is_published │ │
29+
│ tag_ids │╾──┘
30+
└──────────────┘
31+
```
32+
33+
A typical `post` record therefore looks like this:
34+
35+
```json
36+
{
37+
"id": 1,
38+
"title": "Hello world",
39+
"body": "...",
40+
"is_published": true,
41+
"tags_ids": [1, 2, 3]
42+
}
43+
```
44+
45+
In that case, use `<ReferenceArrayFieldBase>` to display the post tag names as a list of chips, as follows:
46+
47+
```jsx
48+
import { ListBase, ListIterator, ReferenceArrayFieldBase } from 'react-admin';
49+
50+
export const PostList = () => (
51+
<ListBase>
52+
<ListIterator>
53+
<ReferenceArrayFieldBase reference="tags" source="tag_ids">
54+
<TagList />
55+
</ReferenceArrayFieldBase>
56+
</ListIterator>
57+
</ListBase>
58+
);
59+
60+
const TagList = (props: { children: React.ReactNode }) => {
61+
const context = useListContext();
62+
63+
if (context.isPending) {
64+
return <p>Loading...</p>;
65+
}
66+
67+
if (context.error) {
68+
return <p className="error">{context.error.toString()}</p>;
69+
}
70+
return (
71+
<p>
72+
{listContext.data?.map((tag, index) => (
73+
<li key={index}>{tag.name}</li>
74+
))}
75+
</p>
76+
);
77+
};
78+
```
79+
80+
`<ReferenceArrayFieldBase>` expects a `reference` attribute, which specifies the resource to fetch for the related records. It also expects a `source` attribute, which defines the field containing the list of ids to look for in the referenced resource.
81+
82+
`<ReferenceArrayFieldBase>` fetches the `tag` resources related to each `post` resource by matching `post.tag_ids` to `tag.id`.
83+
84+
You can change how the list of related records is rendered by passing a custom child reading the `ListContext` (e.g. a [`<DataTable>`](./DataTable.md)) or a render function prop. See the [`children`](#children) and the [`render`](#render) sections for details.
85+
86+
## Props
87+
88+
| Prop | Required | Type | Default | Description |
89+
| -------------- | -------- | --------------------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
90+
| `source` | Required | `string` | - | Name of the property to display |
91+
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'tags' |
92+
| `children` | Optional\* | `Element` | | One or several elements that render a list of records based on a `ListContext` |
93+
| `render` | Optional\* | `(ListContext) => Element` | | A function that takes a list context and renders a list of records |
94+
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records (the filtering is done client-side) |
95+
| `perPage` | Optional | `number` | 1000 | Maximum number of results to display |
96+
| `queryOptions` | Optional | [`UseQuery Options`](https://tanstack.com/query/v5/docs/react/reference/useQuery) | `{}` | `react-query` options for the `getMany` query |
97+
| `sort` | Optional | `{ field, order }` | `{ field: 'id', order: 'DESC' }` | Sort order to use when displaying the related records (the sort is done client-side) |
98+
| `sortBy` | Optional | `string | Function` | `source` | When used in a `List`, name of the field to use for sorting when the user clicks on the column header. |
99+
100+
\* Either one of children or render is required.
101+
102+
## `children`
103+
104+
You can pass any React component as child, to render the list of related records based on the `ListContext`.
105+
106+
```jsx
107+
<ReferenceArrayFieldBase label="Tags" reference="tags" source="tag_ids">
108+
<TagList />
109+
</ReferenceArrayFieldBase>
110+
111+
const TagList = (props: { children: React.ReactNode }) => {
112+
const { isPending, error, data } = useListContext();
113+
114+
if (isPending) {
115+
return <p>Loading...</p>;
116+
}
117+
118+
if (error) {
119+
return <p className="error">{error.toString()}</p>;
120+
}
121+
return (
122+
<p>
123+
{data?.map((tag, index) => (
124+
<li key={index}>{tag.name}</li>
125+
))}
126+
</p>
127+
);
128+
};
129+
```
130+
131+
## `render`
132+
133+
Alternatively to `children`, you can pass a `render` function prop to `<ReferenceArrayFieldBase>`. The `render` prop will receive the `ListContext` as its argument, allowing to inline the rendering logic.
134+
135+
```jsx
136+
<ReferenceArrayFieldBase
137+
label="Tags"
138+
reference="tags"
139+
source="tag_ids"
140+
render={({ isPending, error, data }) => {
141+
if (isPending) {
142+
return <p>Loading...</p>;
143+
}
144+
if (error) {
145+
return <p className="error">{error.toString()}</p>;
146+
}
147+
return (
148+
<p>
149+
{data.map((tag, index) => (
150+
<li key={index}>{tag.name}</li>
151+
))}
152+
</p>
153+
);
154+
}}
155+
/>
156+
```
157+
158+
**Tip**: When receiving a `render` prop, the `<ReferenceArrayFieldBase>` component will ignore the `children` property.
159+
160+
## `filter`
161+
162+
`<ReferenceArrayFieldBase>` fetches all the related records, and displays them all, too. You can use the `filter` prop to filter the list of related records to display (this works by filtering the records client-side, after the fetch).
163+
164+
For instance, to render only tags that are 'published', you can use the following code:
165+
166+
{% raw %}
167+
```jsx
168+
<ReferenceArrayFieldBase
169+
label="Tags"
170+
source="tag_ids"
171+
reference="tags"
172+
filter={{ is_published: true }}
173+
/>
174+
```
175+
{% endraw %}
176+
177+
## `perPage`
178+
179+
`<ReferenceArrayFieldBase>` fetches *all* the related fields, and puts them all in a `ListContext`. If a record has a large number of related records, it may be a good idea to limit the number of displayed records. The `perPage` prop allows to create a client-side pagination for the related records.
180+
181+
For instance, to limit the display of related records to 10, you can use the following code:
182+
183+
```jsx
184+
<ReferenceArrayFieldBase label="Tags" source="tag_ids" reference="tags" perPage={10} />
185+
```
186+
187+
## `queryOptions`
188+
189+
Use the `queryOptions` prop to pass options to [the `dataProvider.getMany()` query](./useGetOne.md#aggregating-getone-calls) that fetches the referenced record.
190+
191+
For instance, to pass [a custom `meta`](./Actions.md#meta-parameter):
192+
193+
{% raw %}
194+
```jsx
195+
<ReferenceArrayFieldBase queryOptions={{ meta: { foo: 'bar' } }} />
196+
```
197+
{% endraw %}
198+
199+
## `reference`
200+
201+
The resource to fetch for the relateds record.
202+
203+
For instance, if the `posts` resource has a `tag_ids` field, set the `reference` to `tags` to fetch the tags related to each post.
204+
205+
```jsx
206+
<ReferenceArrayFieldBase label="Tags" source="tag_ids" reference="tags" />
207+
```
208+
209+
## `sort`
210+
211+
By default, the related records are displayed in the order in which they appear in the `source`. For instance, if the current record is `{ id: 1234, title: 'Lorem Ipsum', tag_ids: [1, 23, 4] }`, a `<ReferenceArrayFieldBase>` on the `tag_ids` field will display tags in the order 1, 23, 4.
212+
213+
`<ReferenceArrayFieldBase>` can force a different order (via a client-side sort after fetch) if you specify a `sort` prop.
214+
215+
For instance, to sort tags by title in ascending order, you can use the following code:
216+
217+
{% raw %}
218+
```jsx
219+
<ReferenceArrayFieldBase
220+
label="Tags"
221+
source="tag_ids"
222+
reference="tags"
223+
sort={{ field: 'title', order: 'ASC' }}
224+
/>
225+
```
226+
{% endraw %}

0 commit comments

Comments
 (0)