Skip to content

Commit

Permalink
update doc latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Sep 16, 2024
1 parent 5eaa128 commit 1784fc6
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 37 deletions.
14 changes: 11 additions & 3 deletions DataProviderWriting.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ interface GetListParams {
pagination: { page: number, perPage: number };
sort: { field: string, order: 'ASC' | 'DESC' };
filter: any;
meta?: any;
meta?: any; // request metadata
signal?: AbortSignal;
}
interface GetListResult {
Expand All @@ -66,6 +66,7 @@ interface GetListResult {
hasNextPage?: boolean;
hasPreviousPage?: boolean;
};
meta?: any; // response metadata
}
function getList(resource: string, params: GetListParams): Promise<GetListResult>
```
Expand All @@ -88,7 +89,13 @@ dataProvider.getList('posts', {
// { id: 123, title: "hello, world", author_id: 12 },
// { id: 125, title: "howdy partner", author_id: 12 },
// ],
// total: 27
// total: 27,
// meta: {
// facets: [
// { name: "published", count: 12 },
// { name: "draft", count: 15 },
// ],
// },
// }
```

Expand Down Expand Up @@ -167,7 +174,7 @@ interface GetManyReferenceParams {
pagination: { page: number, perPage: number };
sort: { field: string, order: 'ASC' | 'DESC' };
filter: any;
meta?: any;
meta?: any; // request metadata
signal?: AbortSignal;
}
interface GetManyReferenceResult {
Expand All @@ -178,6 +185,7 @@ interface GetManyReferenceResult {
hasNextPage?: boolean;
hasPreviousPage?: boolean;
};
meta?: any; // response metadata
}
function getManyReference(resource: string, params: GetManyReferenceParams): Promise<GetManyReferenceResult>
```
Expand Down
2 changes: 1 addition & 1 deletion DateInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Here is how to set up the pickers to use the `fr` locale:
import { Admin, Resource } from 'react-admin';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { fr } from 'date-fns/locale';
import { fr } from 'date-fns/locale/fr';
import { EventEdit } from './events';

export const App = () => (
Expand Down
2 changes: 1 addition & 1 deletion DateTimeInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Here is how to set up the pickers to use the `fr` locale:
import { Admin, Resource } from 'react-admin';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { fr } from 'date-fns/locale';
import { fr } from 'date-fns/locale/fr';
import { EventEdit } from './events';

export const App = () => (
Expand Down
71 changes: 69 additions & 2 deletions List.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ const BoolkList = () => (

By default, react-admin synchronizes the `<List>` parameters (sort, pagination, filters) with the query string in the URL (using `react-router` location) and the [Store](./Store.md).

When you use a `<List>` component anywhere else than as `<Resource list>`, you may want to disable this synchronization to keep the parameters in a local state, independent for each `<List>` instance. This allows to have multiple lists on a single page. The drawback is that a hit on the "back" button doesn't restore the previous list parameters. To do so, pass the `disableSyncWithLocation` prop.
When you use a `<List>` component anywhere else than as `<Resource list>`, you may want to disable this synchronization to keep the parameters in a local state, independent for each `<List>` instance. This allows to have multiple lists on a single page. To do so, pass the `disableSyncWithLocation` prop. The drawback is that a hit on the "back" button doesn't restore the previous list parameters.

{% raw %}
```jsx
Expand Down Expand Up @@ -445,7 +445,35 @@ const Dashboard = () => (
```
{% endraw %}

**Tip**: As `disableSyncWithLocation` also disables the persistence of the list parameters in the Store, any custom string specified in the `storeKey` prop is ignored when `disableSyncWithLocation` is set to `true`.
**Tip**: `disableSyncWithLocation` also disables the persistence of the list parameters in the Store by default. To enable the persistence of the list parameters in the Store, you can pass a custom `storeKey` prop.

```diff
const Dashboard = () => (
<div>
// ...
<ResourceContextProvider value="posts">
- <List disableSyncWithLocation>
+ <List disableSyncWithLocation storeKey="postsListParams">
<SimpleList
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
/>
</List>
</ResourceContextProvider>
<ResourceContextProvider value="comments">
- <List disableSyncWithLocation>
+ <List disableSyncWithLocation storeKey="commentsListParams">
<SimpleList
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
/>
</List>
</ResourceContextProvider>
</div>
)
```

Please note that the selection state is not synced in the URL but in a global store using the resource as key. Thus, all lists in the page using the same resource will share the same synced selection state. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. If you want to allow custom `storeKey`'s for managing selection state, you will have to implement your own `useListController` hook and pass a custom key to the `useRecordSelection` hook. You will then need to implement your own `DeleteButton` and `BulkDeleteButton` to manually unselect rows when deleting records. You can still opt out of all store interactions including selection if you set it to `false`.

Expand Down Expand Up @@ -1112,6 +1140,45 @@ const ProductList = () => (
)
```

## Accessing Extra Response Data

If `dataProvider.getList()` returns additional metadata in the response under the `meta` key, you can access it in the list view using the `meta` property of the `ListContext`.

This is often used by APIs to return statistics or other metadata about the list of records.

```tsx
// dataProvider.getLists('posts') returns response like
// {
// data: [ ... ],
// total: 293,
// meta: {
// facets: [
// { value: 'Novels', count: 245 },
// { value: 'Essays', count: 23 },
// { value: 'Short stories', count: 25 },
// ],
// },
// }
const Facets = () => {
const { isLoading, error, meta } = useListContext();
if (isLoading || error) return null;
const facets = meta.facets;
return (
<Stack direction="row" gap={3} mt={2} ml={1}>
{facets.map(facet => (
<Badge
key={facet.value}
badgeContent={facet.count}
color="primary"
>
<Chip label={facet.value} size="small" />
</Badge>
))}
</Stack>
);
};
```

## Controlled Mode

`<List>` deduces the resource and the list parameters from the URL. This is fine for a page showing a single list of records, but if you need to display more than one list in a page, you probably want to define the list parameters yourself.
Expand Down
4 changes: 1 addition & 3 deletions OldVersions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ You have two options to read them:

## v5

- [v5.2](https://github.com/marmelab/react-admin/blob/next/docs/Admin.md)
- [v5.1](https://github.com/marmelab/react-admin/blob/v5.1.5/docs/Admin.md)
- [v5.0](https://github.com/marmelab/react-admin/blob/v5.0.5/docs/Admin.md)
- [v5.0](https://github.com/marmelab/react-admin/blob/next/docs/Admin.md)

## v4

Expand Down
1 change: 1 addition & 0 deletions Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ title: "Index"
* [`<DatagridHeader>`](./Datagrid.md#header)
* [`<DateField>`](./DateField.md)
* [`<DateInput>`](./DateInput.md)
* [`<DateRangeInput>`](./DateRangeInput.md)
* [`<DateTimeInput>`](./DateTimeInput.md)
* `<DeleteButton>`
* [`<DualListInput>`](./DualListInput.md)<img class="icon" src="./img/premium.svg" />
Expand Down
39 changes: 39 additions & 0 deletions ReferenceManyField.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ This example leverages [`<SingleFieldList>`](./SingleFieldList.md) to display an
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'books' |
| `sort` | Optional | `{ field, order }` | `{ field: 'id', order: 'DESC' }` | Sort order to use when fetching the related records, passed to `getManyReference()` |
| `source` | Optional | `string` | `id` | Target field carrying the relationship on the source record (usually 'id') |
| `storeKey` | Optional | `string` | - | The key to use to store the records selection state |
| `target` | Required | `string` | - | Target field carrying the relationship on the referenced resource, e.g. 'user_id' |

`<ReferenceManyField>` also accepts the [common field props](./Fields.md#common-field-props), except `emptyText` (use the child `empty` prop instead).
Expand Down Expand Up @@ -301,6 +302,44 @@ By default, `ReferenceManyField` uses the `id` field as target for the reference
</ReferenceManyField>
```

## `storeKey`

By default, react-admin stores the reference list selection state in localStorage so that users can come back to the list and find it in the same state as when they left it. React-admin uses the main resource, record id and reference resource as the identifier to store the selection state (under the key `${resource}.${record.id}.${reference}.selectedIds`).

If you want to display multiple lists of the same reference and keep distinct selection states for each one, you must give each list a unique `storeKey` property.

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.

{% raw %}
```jsx
<Stack direction="row" spacing={2}>
<ReferenceManyField
reference="books"
target="author_id"
queryOptions={{
meta: { foo: 'bar' },
}}
>
<Datagrid>
<TextField source="title" />
</Datagrid>
</ReferenceManyField>
<ReferenceManyField
reference="books"
target="author_id"
queryOptions={{
meta: { foo: 'bar' },
}}
storeKey="custom"
>
<Datagrid>
<TextField source="title" />
</Datagrid>
</ReferenceManyField>
</Stack>
```
{% endraw %}

## `target`

Name of the field carrying the relationship on the referenced resource. For instance, if an `author` has many `books`, and each book resource exposes an `author_id` field, the `target` would be `author_id`.
Expand Down
15 changes: 2 additions & 13 deletions Remix.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export default defineConfig({
{
// ...
"resolutions": {
"react-router": "6.24.1",
"react-router-dom": "6.24.1"
"react-router": "6.26.1",
"react-router-dom": "6.26.1"
}
}
```
Expand All @@ -88,11 +88,6 @@ To do so, add a [splat route](https://remix.run/docs/en/main/file-conventions/ro
// in app/routes/admin.$.tsx
import { Admin, Resource, ListGuesser } from "react-admin";
import jsonServerProvider from "ra-data-json-server";
import styles from "~/styles/admin.css";

export function links() {
return [{ rel: "stylesheet", href: styles }];
}

const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");

Expand All @@ -106,12 +101,6 @@ export default function App() {
}
```

The stylesheet link is necessary to reset the default styles of the admin app. Create it in `app/styles/admin.css`:

```css
body { margin: 0; }
```

**Tip** Don't forget to set the `<Admin basename>` prop, so that react-admin generates links relative to the "/admin" subpath:

You can now start the app in `development` mode with `npm run dev`. The admin should render at `http://localhost:3000/admin`, and you can use the Remix routing system to add more pages.
Expand Down
26 changes: 16 additions & 10 deletions Vite.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ title: "Installing React-admin With Vite"
Create a new Vite project with React template using the command line:

```sh
yarn create vite my-admin --template react
yarn create vite my-admin --template react-ts
```

We recommend using the TypeScript template:
**Tip**: If you prefer using JavaScript instead of TypeScript, change the template to `react`.

```sh
yarn create vite my-admin --template react-ts
yarn create vite my-admin --template react
```

## Setting Up React-Admin
Expand Down Expand Up @@ -61,16 +61,22 @@ const App = () => <MyAdmin />;
export default App;
```

Then, change the `index.css` file to look like this:
Remove the `index.css` import in the `main.tsx` file:

```css
/* in src/index.css */
body {
margin: 0;
}
```diff
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
-import './index.css'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
```

Next, add the `Roboto` font to your `index.html` file:
Finally, add the `Roboto` font to your `index.html` file:

```diff
// in ./index.html
Expand Down
3 changes: 2 additions & 1 deletion WithListContext.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ As a reminder, the [`ListContext`](./useListContext.md) is an object with the fo
<WithListContext render={({
// fetched data
data, // an array of the list records, e.g. [{ id: 123, title: 'hello world' }, { ... }]
total, // the total number of results for the current filters, excluding pagination. Useful to build the pagination controls, e.g. 23
total, // the total number of results for the current filters, excluding pagination. Useful to build the pagination controls, e.g. 23
meta, // Additional information about the list, like facets & statistics
isPending, // boolean that is true until the data is available for the first time
isLoading, // boolean that is true until the data is fetched for the first time
isFetching, // boolean that is true while the data is being fetched, and false once the data is fetched
Expand Down
Binary file added img/DateRangeInput.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
<li {% if page.path == 'BooleanInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./BooleanInput.html"><code>&lt;BooleanInput&gt;</code></a></li>
<li {% if page.path == 'CheckboxGroupInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./CheckboxGroupInput.html"><code>&lt;CheckboxGroupInput&gt;</code></a></li>
<li {% if page.path == 'DateInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./DateInput.html"><code>&lt;DateInput></code></a></li>
<li {% if page.path == 'DateRangeInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DateRangeInput.html"><code>&lt;DateRangeInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
<li {% if page.path == 'DateTimeInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DateTimeInput.html"><code>&lt;DateTimeInput&gt;</code></a></li>
<li {% if page.path == 'DualListInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DualListInput.html"><code>&lt;DualListInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
<li {% if page.path == 'FileInput.md' %} class="active" {% endif %}><a class="nav-link" href="./FileInput.html"><code>&lt;FileInput&gt;</code></a></li>
Expand Down
41 changes: 39 additions & 2 deletions useGetList.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This hook calls `dataProvider.getList()` when the component mounts. It's ideal f
## Syntax

```jsx
const { data, total, isPending, error, refetch } = useGetList(
const { data, total, isPending, error, refetch, meta } = useGetList(
resource,
{
pagination: { page, perPage },
Expand All @@ -23,7 +23,7 @@ const { data, total, isPending, error, refetch } = useGetList(
);
```

The `meta` argument is optional. It can be anything you want to pass to the data provider, e.g. a list of fields to show in the result.
The `meta` argument is optional. It can be anything you want to pass to the data provider, e.g. a list of fields to show in the result. It is distinct from the `meta` property of the response, which may contain additional metadata returned by the data provider.

The `options` parameter is optional, and is passed to [react-query's `useQuery` hook](https://tanstack.com/query/v5/docs/react/reference/useQuery). It may contain the following options:

Expand Down Expand Up @@ -134,6 +134,43 @@ const LatestNews = () => {

In this example, the `useGetList` hook fetches all the posts, and displays a list of the 10 most recent posts in a `<Datagrid>`. The `<Pagination>` component allows the user to navigate through the list. Users can also sort the list by clicking on the column headers.

## Passing Additional Arguments

If you need to pass additional arguments to the data provider, you can pass them in the `meta` argument.

For example, if you want to embed related records in the response, and your data provider supports the `embed` meta parameter, you can pass it like this:

```jsx
const { data, total, isPending, error } = useGetList(
'posts',
{
pagination: { page: 1, perPage: 10 },
sort: { field: 'published_at', order: 'DESC' },
// Pass extra parameters using the meta argument
meta: { embed: ['author', 'category'] }
}
);
```

**Tip**: Don't mix the `meta` parameter with the `meta` property of the response (see below). Although they share the same name, they are not related.

## Accessing Response Metadata

If your backend returns additional metadata along with the records, you can access it in the `meta` property of the result.

```jsx
const {
data,
total,
isPending,
error,
// access the extra response details in the meta property
meta
} = useGetList('posts', { pagination: { page: 1, perPage: 10 }});
```

**Tip**: Don't mix the `meta` property of the response with the `meta` parameter (see above). Although they share the same name, they are not related.

## Partial Pagination

If your data provider doesn't return the `total` number of records (see [Partial Pagination](./DataProviderWriting.md#partial-pagination)), you can use the `pageInfo` field to determine if there are more records to fetch.
Expand Down
Loading

0 comments on commit 1784fc6

Please sign in to comment.