Skip to content

Commit 625d03d

Browse files
authored
Merge pull request #11119 from marmelab/exporterbutton-listcontext
Improve `<ExportButton>` with list-context getData
2 parents fea04cb + 6f8d02e commit 625d03d

30 files changed

Lines changed: 621 additions & 71 deletions

docs/ArrayField.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ storybook_path: ra-ui-materialui-fields-arrayfield--basic
66

77
# `<ArrayField>`
88

9-
`<ArrayField>` renders an embedded array of objects.
9+
`<ArrayField>` renders an embedded array of objects.
1010

1111
![ArrayField](./img/array-field.webp)
1212

@@ -76,6 +76,7 @@ const PostShow = () => (
7676
|------------|----------|-------------------|---------|------------------------------------------|
7777
| `children` | Required | `ReactNode` | | The component to render the list. |
7878
| `filter` | Optional | `object` | | The filter to apply to the list. |
79+
| `exporter` | Optional | `function` | `default Exporter` | The function called by export buttons in the list context. |
7980
| `perPage` | Optional | `number` | 1000 | The number of items to display per page. |
8081
| `sort` | Optional | `{ field, order}` | | The sort to apply to the list. |
8182

@@ -158,6 +159,7 @@ const PostShow = () => (
158159
You can use the `filter` prop to display only a subset of the items in the array. For instance, to display only the backlinks for a particular day:
159160

160161
{% raw %}
162+
161163
```jsx
162164
<ArrayField source="backlinks" filter={{ date: '2012-08-10T00:00:00.000Z' }}>
163165
<DataTable>
@@ -167,13 +169,14 @@ You can use the `filter` prop to display only a subset of the items in the array
167169
</DataTable>
168170
</ArrayField>
169171
```
172+
170173
{% endraw %}
171174

172175
The filtering capabilities are very limited. For instance, there is no "greater than" or "less than" operator. You can only filter on the equality of a field.
173176

174177
## `perPage`
175178

176-
If the value is a large array, and you don't need to display all the items, you can use the `perPage` prop to limit the number of items displayed.
179+
If the value is a large array, and you don't need to display all the items, you can use the `perPage` prop to limit the number of items displayed.
177180

178181
As `<ArrayField>` creates a [`ListContext`](./useListContext.md), you can use the `<Pagination>` component to navigate through the items.
179182

@@ -209,13 +212,15 @@ const PostShow = () => (
209212
By default, `<ArrayField>` displays the items in the order they are stored in the field. You can use the `sort` prop to change the sort order.
210213

211214
{% raw %}
215+
212216
```jsx
213217
<ArrayField source="tags" sort={{ field: 'name', order: 'ASC' }}>
214218
<SingleFieldList linkType={false}>
215219
<ChipField source="name" />
216220
</SingleFieldList>
217221
</ArrayField>
218222
```
223+
219224
{% endraw %}
220225

221226
## Using The List Context

docs/ReferenceArrayField.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ You can change how the list of related records is rendered by passing a custom c
9090
| `empty` | Optional | `ReactNode` | - | The component to render when the related records list is empty |
9191
| `error` | Optional | `ReactNode` | - | The component to render when an error occurs while fetching the related records |
9292
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records (the filtering is done client-side) |
93+
| `exporter` | Optional | `function` | `defaultExporter` | The function called by export buttons in the list context |
9394
| `loading` | Optional | `ReactNode` | `<LinearProgress>` | The component to render while fetching the related records |
9495
| `offline` | Optional | `ReactNode` | `<Offline variant="inline" />` | The component to render when there is no connectivity and the record isn't in the cache |
9596
| `pagination` | Optional | `ReactNode` | - | Pagination element to display pagination controls. empty by default (no pagination) |

docs/ReferenceManyField.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ storybook_path: ra-ui-materialui-fields-referencemanyfield--basic
66

77
# `<ReferenceManyField>`
88

9-
`<ReferenceManyField>` is useful for displaying a list of related records via a one-to-many relationship, when the foreign key is carried by the referenced resource.
9+
`<ReferenceManyField>` is useful for displaying a list of related records via a one-to-many relationship, when the foreign key is carried by the referenced resource.
1010

1111
<iframe src="https://www.youtube-nocookie.com/embed/UeM31-65Wc4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="aspect-ratio: 16 / 9;width:100%;margin-bottom:1em;" referrerpolicy="strict-origin-when-cross-origin"></iframe>
1212

@@ -97,6 +97,7 @@ This example leverages [`<SingleFieldList>`](./SingleFieldList.md) to display an
9797
| `empty` | Optional | `ReactNode` | - | Element to display when there are no related records. |
9898
| `error` | Optional | `ReactNode` | - | The component to render when an error occurs while fetching the related records |
9999
| `filter` | Optional | `Object` | - | Filters to use when fetching the related records, passed to `getManyReference()` |
100+
| `exporter` | Optional | `function` | `default Exporter` | The function called by export buttons in the list context |
100101
| `loading` | Optional | `ReactNode` | - | The component to render while fetching the related records |
101102
| `offline` | Optional | `ReactNode` | - | Element to display when there are no related records because of lack of network connectivity. |
102103
| `pagination` | Optional | `Element` | - | Pagination element to display pagination controls. empty by default (no pagination) |
@@ -120,7 +121,7 @@ This example leverages [`<SingleFieldList>`](./SingleFieldList.md) to display an
120121
- [`<SimpleList>`](./SimpleList.md)
121122
- [`<EditableDatagrid>`](./EditableDatagrid.md)
122123
- [`<Calendar>`](./Calendar.md)
123-
- Or a component of your own (check the [`<WithListContext>`](./WithListContext.md) and the [`useListContext`](./useListContext.md) chapters to learn how).
124+
- Or a component of your own (check the [`<WithListContext>`](./WithListContext.md) and the [`useListContext`](./useListContext.md) chapters to learn how).
124125

125126
For instance, use a `<DataTable>` to render the related records in a table:
126127

@@ -451,9 +452,11 @@ Use the `queryOptions` prop to pass options to [the `dataProvider.getMany()` que
451452
For instance, to pass [a custom `meta`](./Actions.md#meta-parameter):
452453

453454
{% raw %}
455+
454456
```jsx
455457
<ReferenceManyField queryOptions={{ meta: { foo: 'bar' } }} />
456458
```
459+
457460
{% endraw %}
458461

459462
## `reference`
@@ -505,6 +508,7 @@ This allows to inline the render logic for the list of related records.
505508
By default, it orders the possible values by id desc. You can change this order by setting the `sort` prop (an object with `field` and `order` properties).
506509

507510
{% raw %}
511+
508512
```jsx
509513
<ReferenceManyField
510514
target="post_id"
@@ -514,6 +518,7 @@ By default, it orders the possible values by id desc. You can change this order
514518
...
515519
</ReferenceManyField>
516520
```
521+
517522
{% endraw %}
518523

519524
## `source`
@@ -539,6 +544,7 @@ If you want to display multiple lists of the same reference and keep distinct se
539544
In the example below, both lists use the same reference ('books'), but their selection states are stored separately (under the store keys `'authors.1.books.selectedIds'` and `'custom.selectedIds'` respectively). This allows to use both components in the same page, each having its own state.
540545

541546
{% raw %}
547+
542548
```jsx
543549
<Stack direction="row" spacing={2}>
544550
<ReferenceManyField
@@ -566,6 +572,7 @@ In the example below, both lists use the same reference ('books'), but their sel
566572
</ReferenceManyField>
567573
</Stack>
568574
```
575+
569576
{% endraw %}
570577

571578
## `target`
@@ -583,11 +590,12 @@ Name of the field carrying the relationship on the referenced resource. For inst
583590

584591
## Rendering Only One Record
585592

586-
Although you are in a one-to-many relationship, you may want to render only one record. For instance, in a book with several reviews, you may want to display only the last. Or, for a product with many prices, you may want to display only the one in euros.
593+
Although you are in a one-to-many relationship, you may want to render only one record. For instance, in a book with several reviews, you may want to display only the last. Or, for a product with many prices, you may want to display only the one in euros.
587594

588595
In these cases, use [the `<ReferenceOneField>` component](./ReferenceOneField.md) instead of `<ReferenceManyField>`.
589596

590597
{% raw %}
598+
591599
```jsx
592600
<ReferenceOneField
593601
label="Latest review"
@@ -599,9 +607,11 @@ In these cases, use [the `<ReferenceOneField>` component](./ReferenceOneField.md
599607
<TextField source="body" />
600608
</ReferenceOneField>
601609
```
610+
602611
{% endraw %}
603612

604613
{% raw %}
614+
605615
```jsx
606616
<ReferenceOneField
607617
label="Price (€)"
@@ -612,6 +622,7 @@ In these cases, use [the `<ReferenceOneField>` component](./ReferenceOneField.md
612622
<NumberField source="price" />
613623
</ReferenceOneField>
614624
```
625+
615626
{% endraw %}
616627

617628
## Adding or editing a related record
@@ -624,6 +635,7 @@ To allow users to create or edit a record without leaving the current view, use
624635
</video>
625636

626637
{% raw %}
638+
627639
```jsx
628640
import { Edit, SimpleForm, TextInput, ReferenceManyField, WithRecord, DataTable } from 'react-admin';
629641
import { CreateInDialogButton, EditInDialogButton } from "@react-admin/ra-form-layout";
@@ -667,4 +679,5 @@ const EmployerEdit = () => (
667679
</Edit>
668680
)
669681
```
682+
670683
{% endraw %}

docs/useList.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const MyComponent = () => {
6666
`useList` expects an object with the following keys:
6767

6868
* [`data`](#data)
69+
* [`exporter`](#exporter)
6970
* [`filter`](#filter)
7071
* [`filterCallback`](#filtercallback)
7172
* [`isFetching`](#isfetching)
@@ -88,6 +89,23 @@ const { data } = useList({
8889
});
8990
```
9091

92+
## `exporter`
93+
94+
The function called by export buttons in this list context. Defaults to `defaultExporter`.
95+
96+
```jsx
97+
import { downloadCSV } from 'react-admin';
98+
import jsonExport from 'jsonexport/dist';
99+
100+
const exporter = records => {
101+
jsonExport(records, (err, csv) => {
102+
downloadCSV(csv, 'actors');
103+
});
104+
};
105+
106+
const listContext = useList({ data, exporter });
107+
```
108+
91109
## `filter`
92110

93111
The initial filter to apply to the data.

docs/useListContext.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const {
9292
defaultTitle, // Translated title based on the resource, e.g. 'Posts'
9393
resource, // Resource name, deduced from the location. e.g. 'posts'
9494
refetch, // Callback for fetching the list data again
95+
getData, // Callback that returns the full list data (ignores pagination)
9596
} = useListContext();
9697
```
9798

docs/useListController.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ storybook_path: ra-core-controller-list-uselistcontroller--authenticated
66

77
# `useListController`
88

9-
`useListController` contains the headless logic of the [`<List>`](./List.md) component. It's useful to create a custom List view. It's also the base hook when building a custom view with another UI kit than Material UI.
9+
`useListController` contains the headless logic of the [`<List>`](./List.md) component. It's useful to create a custom List view. It's also the base hook when building a custom view with another UI kit than Material UI.
1010

1111
![List view built with Ant Design](./img/list_ant_design.png)
1212

13-
`useListController` reads the list parameters from the URL, calls `dataProvider.getList()`, prepares callbacks for modifying the pagination, filters, sort and selection, and returns them together with the data. Its return value matches the [`ListContext`](./useListContext.md) shape.
13+
`useListController` reads the list parameters from the URL, calls `dataProvider.getList()`, prepares callbacks for modifying the pagination, filters, sort and selection, and returns them together with the data. Its return value matches the [`ListContext`](./useListContext.md) shape.
1414

1515
`useListController` is used internally by [`<List>`](./List.md) and [`<ListBase>`](./ListBase.md). If your list view uses react-admin components like [`<DataTable>`](./DataTable.md), prefer [`<ListBase>`](./ListBase.md) to `useListController` as it takes care of creating a `<ListContext>`.
1616

@@ -21,6 +21,7 @@ storybook_path: ra-core-controller-list-uselistcontroller--authenticated
2121
Here the code for the post list view above, built with [Ant Design](https://ant.design/):
2222

2323
{% raw %}
24+
2425
```jsx
2526
import { useListController } from 'react-admin';
2627
import { Card, Table, Button } from 'antd';
@@ -86,6 +87,7 @@ const columns = [
8687

8788
export default PostList;
8889
```
90+
8991
{% endraw %}
9092

9193
When using react-admin components, it's common to call `useListController()` without parameters, and to put the result in a `ListContext` to make it available to the rest of the component tree.
@@ -110,17 +112,21 @@ const MyList = () => {
110112

111113
`useListController` expects an object as parameter. All keys are optional.
112114

113-
* [`debounce`](./List.md#debounce): Debounce time in ms for the setFilters callbacks
114-
* [`disableAuthentication`](./List.md#disableauthentication): Set to true to allow anonymous access to the list
115-
* [`disableSyncWithLocation`](./List.md#disablesyncwithlocation): Set to true to have more than one list per page
116-
* [`exporter`](./List.md#exporter): Exporter function
117-
* [`filter`](./List.md#filter-permanent-filter): Permanent filter, forced over the user filter
118-
* [`filterDefaultValues`](./List.md#filterdefaultvalues): Default values for the filter form
119-
* [`perPage`](./List.md#perpage): Number of results per page
120-
* [`queryOptions`](./List.md#queryoptions): React-query options for the useQuery call
121-
* [`resource`](./List.md#resource): Resource name, e.g. 'posts' ; defaults to the current resource context
122-
* [`sort`](./List.md#sort): Current sort value, e.g. `{ field: 'published_at', order: 'DESC' }`
123-
* [`storeKey`](#storekey): Key used to differentiate the list from another sharing the same resource, in store managed states
115+
| Prop | Type | Default | Description |
116+
|---------------------------|-------------------------|---------|--------------------------------------------------------------------------------------------------|
117+
| `debounce` | `number` | `500` | The debounce delay in milliseconds to apply when users change the sort or filter parameters. |
118+
| `disableAuthentication` | `boolean` | `false` | Set to `true` to disable the authentication check. |
119+
| `disableSyncWithLocation` | `boolean` | `false` | Set to `true` to disable the synchronization of the list parameters with the URL. |
120+
| `exporter` | `function` | - | The function to call to export the list. |
121+
| `filter` | `object` | - | The permanent filter values, forced over the user filter |
122+
| `filterDefaultValues` | `object` | - | The default filter values. |
123+
| `perPage` | `number` | `10` | The number of records to fetch per page. |
124+
| `queryOptions` | `object` | - | The options to pass to the `useQuery` hook. |
125+
| `resource` | `string` | - | The resource name, e.g. `posts`. |
126+
| `sort` | `object` | - | The initial sort parameters, e.g. `{ field: 'published_at', order: 'DESC' }`. |
127+
| `storeKey` | `string` &#124; `false` | - | The key to use to store the current filter & sort. Pass `false` to disable store synchronization |
128+
129+
Check [the `<List>` component documentation](./List.md) for more details about these parameters.
124130

125131
Here are their default values:
126132

@@ -178,6 +184,7 @@ If you want to disable the storage of list parameters altogether for a given lis
178184
In the example below, both lists `TopPosts` and `FlopPosts` use the same resource ('posts'), but their controller states are stored separately (under the store keys `'top'` and `'flop'` respectively).
179185

180186
{% raw %}
187+
181188
```jsx
182189
import { useListController } from 'react-admin';
183190

@@ -211,14 +218,14 @@ const FlopPosts = (
211218
<OrderedPostList storeKey="flop" sort={{ field: 'votes', order: 'ASC' }} />
212219
);
213220
```
221+
214222
{% endraw %}
215223

216224
You can disable this feature by setting the `storeKey` prop to `false`. When disabled, parameters will not be persisted in the store.
217225

218-
219226
## Return Value
220227

221-
`useListController` returns an object with the following keys:
228+
`useListController` returns an object with the following keys:
222229

223230
```jsx
224231
const {
@@ -253,16 +260,17 @@ const {
253260
defaultTitle, // Translated title based on the resource, e.g. 'Posts'
254261
resource, // Resource name, deduced from the location. e.g. 'posts'
255262
refetch, // Callback for fetching the list data again
263+
getData, // Callback that returns the full list data (ignores pagination)
256264
} = useListController();
257265
```
258266

259267
## Using `setFilters` to Update Filters
260268

261269
The `setFilters` method is used to update the filters. It takes three arguments:
262270

263-
- `filters`: an object containing the new filter values
264-
- `displayedFilters`: an object containing the new displayed filters
265-
- `debounced`: set to true to debounce the call to setFilters (false by default)
271+
* `filters`: an object containing the new filter values
272+
* `displayedFilters`: an object containing the new displayed filters
273+
* `debounced`: set to true to debounce the call to setFilters (false by default)
266274

267275
You can use it to update the list filters:
268276

0 commit comments

Comments
 (0)