Skip to content

Commit 2de6341

Browse files
committed
Merge branch 'master' into next
2 parents 2ba1258 + b6b10e5 commit 2de6341

130 files changed

Lines changed: 2650 additions & 1128 deletions

File tree

Some content is hidden

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

.eslintrc

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
21
{
32
"parser": "@typescript-eslint/parser",
43
"parserOptions": {
5-
"warnOnUnsupportedTypeScriptVersion": false
4+
"warnOnUnsupportedTypeScriptVersion": false,
65
},
76
"extends": ["react-app", "plugin:prettier/recommended"],
87
"plugins": [
@@ -11,7 +10,7 @@
1110
"jsx-a11y",
1211
"prettier",
1312
"react",
14-
"react-hooks"
13+
"react-hooks",
1514
],
1615
"rules": {
1716
"no-use-before-define": "off",
@@ -23,23 +22,31 @@
2322
{
2423
"name": "@mui/material",
2524
"importNames": ["makeStyles", "createMuiTheme"],
26-
"message": "Please import from @mui/material/styles instead. See https://material-ui.com/guides/minimizing-bundle-size/#option-2 for more information"
27-
}
28-
]
29-
}
25+
"message": "Please import from @mui/material/styles instead. See https://material-ui.com/guides/minimizing-bundle-size/#option-2 for more information",
26+
},
27+
{
28+
"name": "@mui/icons-material",
29+
"message": "Named import from @mui/icons-material should be avoided for performance reasons. Use a default import instead. E.g. `import Dashboard from '@mui/icons-material/Dashboard';` instead of `import { Dashboard } from '@mui/icons-material';`.See https://mui.com/material-ui/guides/minimizing-bundle-size/#development-environment for more information.",
30+
},
31+
{
32+
"name": "lodash",
33+
"message": "Named import from lodash should be avoided for performance reasons. Use a default import instead. E.g. `import merge from 'lodash/merge';` instead of `import { merge } from 'lodash';`.",
34+
},
35+
],
36+
},
3037
],
3138
"no-redeclare": "off",
3239
"import/no-anonymous-default-export": "off",
3340
"@typescript-eslint/no-redeclare": ["error"],
3441
"no-unused-vars": "off",
3542
"@typescript-eslint/no-unused-vars": [
3643
"warn", // or "error"
37-
{
44+
{
3845
"argsIgnorePattern": "^_",
3946
"varsIgnorePattern": "^_",
4047
"caughtErrorsIgnorePattern": "^_",
41-
"ignoreRestSiblings": true
42-
}
43-
]
44-
}
48+
"ignoreRestSiblings": true,
49+
},
50+
],
51+
},
4552
}

.github/ISSUE_TEMPLATE/Bug_report.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ about: Something isn't working as expected. Please tell us!
2222
<!-- If you are able to illustrate the bug or feature request with an example, please provide a sample application via one of the following means: -->
2323

2424
* Preferably, a sandbox forked from
25+
- https://codesandbox.io/p/github/marmelab/react-admin-sandbox/main (v5)
26+
2527
- https://stackblitz.com/github/marmelab/react-admin/tree/master/examples/simple (v5)
2628
- https://stackblitz.com/github/marmelab/react-admin/tree/4.x/examples/simple (v4)
2729
* A link to a GitHub repo with the minimal codebase to reproduce the issue

CHANGELOG.md

Lines changed: 46 additions & 74 deletions
Large diffs are not rendered by default.

docs/AppBar.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,26 @@ The content of the user menu depends on the return value of `authProvider.getIde
248248

249249
You can customize the user menu by passing a `userMenu` prop to the `<AppBar>` component.
250250

251-
```jsx
251+
```tsx
252252
import * as React from 'react';
253-
import { AppBar, UserMenu, useUserMenu } from 'react-admin';
253+
import { AppBar, Logout, UserMenu, useUserMenu } from 'react-admin';
254254
import { MenuItem, ListItemIcon, ListItemText } from '@mui/material';
255255
import SettingsIcon from '@mui/icons-material/Settings';
256+
import { Link } from "react-router-dom";
256257

257258
// It's important to pass the ref to allow Material UI to manage the keyboard navigation
258-
const SettingsMenuItem = React.forwardRef((props, ref) => {
259-
// We are not using MenuItemLink so we retrieve the onClose function from the UserContext
260-
const { onClose } = useUserMenu();
259+
const SettingsMenuItem = React.forwardRef<HTMLAnchorElement>((props, ref) => {
260+
const userMenuContext = useUserMenu();
261+
if (!userMenuContext) {
262+
throw new Error("<SettingsMenuItem> should be used inside a <UserMenu>");
263+
}
264+
const { onClose } = userMenuContext;
261265
return (
262266
<MenuItem
263267
onClick={onClose}
264268
ref={ref}
269+
component={Link}
270+
to="/settings"
265271
// It's important to pass the props to allow Material UI to manage the keyboard navigation
266272
{...props}
267273
>
@@ -273,7 +279,7 @@ const SettingsMenuItem = React.forwardRef((props, ref) => {
273279
);
274280
});
275281

276-
const MyAppBar = () => (
282+
export const MyAppBar = () => (
277283
<AppBar
278284
userMenu={
279285
<UserMenu>

docs/ArrayInput.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ Check [the `<SimpleFormIterator>` documentation](./SimpleFormIterator.md) for de
7979

8080
**Tip**: If you need to edit an array of *related* records, i.e. if the `items` above actually come from another resource, you should use a [`<ReferenceManyInput>`](./ReferenceManyInput.md) instead.
8181

82+
**Note**: Using [`shouldUnregister`](https://react-hook-form.com/docs/useform#shouldUnregister) should be avoided when using `<ArrayInput>` (which internally uses `useFieldArray`) as the unregister function gets called after input unmount/remount and reorder. This limitation is mentioned in the react-hook-form [documentation](https://react-hook-form.com/docs/usecontroller#props). If you are in such a situation, you can use the [`transform`](https://marmelab.com/react-admin/Edit.html#transform) prop to manually clean the submitted values.
83+
8284
## Props
8385

8486
`<ArrayInput>` accepts the [common input props](./Inputs.md#common-input-props) (except `format` and `parse`).

docs/Authentication.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ export const dataProvider = addRefreshAuthToDataProvider(baseDataProvider, refre
416416

417417
## Authorization
418418

419-
Access control and permissions allow you to restrict certain pages to specific users. React-admin provides powerful primitives for implementing authorization logic. For detailed guidance, check out the [Authorization](./Authorization.md) documentation.
419+
Access control and permissions allow you to restrict certain pages to specific users. React-admin provides powerful primitives for implementing authorization logic. For detailed guidance, check out the [Authorization](./Permissions.md) documentation.
420420

421421
<video controls autoplay muted loop>
422422
<source src="./img/AccessControl.mp4" type="video/mp4"/>

docs/AutocompleteArrayInput.md

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ The form value for the source must be an array of the selected values, e.g.
6060
|----------------------------|----------|-----------------------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
6161
| `choices` | Required | `Object[]` | - | List of choices |
6262
| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice |
63-
| `createLabel` | Optional | `string` | `ra.action. create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty |
64-
| `createItemLabel` | Optional | `string` | `ra.action .create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty |
63+
| `createLabel` | Optional | `string` | - | The label used as hint to let users know they can create a new choice. Displayed when the filter is empty. |
64+
| `createItemLabel` | Optional | `string` | `ra.action .create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty. |
6565
| `debounce` | Optional | `number` | `250` | The delay to wait before calling the setFilter function injected when used in a ReferenceArray Input. |
6666
| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element |
6767
| `filterToQuery` | Optional | `string` => `Object` | `q => ({ q })` | How to transform the searchText into a parameter for the data provider |
@@ -159,7 +159,7 @@ const choices = [
159159
const UserCreate = () => (
160160
<Create>
161161
<SimpleForm>
162-
<SelectArrayInput
162+
<AutocompleteArrayInput
163163
source="roles"
164164
choices={choices}
165165
create={<CreateRole />}
@@ -216,6 +216,37 @@ If you just need to ask users for a single string to create the new option, you
216216

217217
If you're in a `<ReferenceArrayInput>` or `<ReferenceManyToManyInput>`, the `handleSubmit` will need to create a new record in the related resource. Check the [Creating New Choices](#creating-new-choices) for an example.
218218

219+
## `createLabel`
220+
221+
When you set the `create` or `onCreate` prop, `<AutocompleteArrayInput>` lets users create new options.
222+
You can use the `createLabel` prop to render an additional (disabled) menu item at the bottom of the list, that will only appear when the input is empty, inviting users to start typing to create a new option.
223+
224+
```jsx
225+
<AutocompleteArrayInput
226+
source="roles"
227+
choices={choices}
228+
create={<CreateRole />}
229+
createLabel="Start typing to create a new item"
230+
/>
231+
```
232+
233+
## `createItemLabel`
234+
235+
If you set the `create` or `onCreate` prop, `<AutocompleteArrayInput>` lets users create new options. When the text entered by the user doesn't match any option, the input renders a "Create XXX" menu item at the bottom of the list.
236+
237+
You can customize the label of that menu item by setting a custom translation for the `ra.action.create_item` key in the translation files.
238+
239+
Or, if you want to customize it just for this `<AutocompleteArrayInput>`, use the `createItemLabel` prop:
240+
241+
```jsx
242+
<AutocompleteArrayInput
243+
source="roles"
244+
choices={choices}
245+
create={<CreateRole />}
246+
createItemLabel="Add a new role: %{item}"
247+
/>
248+
```
249+
219250
## `debounce`
220251

221252
When used inside a [`<ReferenceArrayInput>`](./ReferenceArrayInput.md), `<AutocompleteArrayInput>` will call `dataProvider.getList()` with the current input value as filter after a delay of 250ms. This is to avoid calling the API too often while users are typing their query.

docs/AutocompleteInput.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ The form value for the source must be the selected value, e.g.
6060
|--------------------------- |----------|---------------------- |---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
6161
| `choices` | Optional | `Object[]` | `-` | List of items to autosuggest. Required if not inside a ReferenceInput. |
6262
| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice |
63-
| `createLabel` | Optional | `string` | `ra.action .create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty |
64-
| `createItemLabel` | Optional | `string` | `ra.action .create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty |
63+
| `createLabel` | Optional | `string` | - | The label used as hint to let users know they can create a new choice. Displayed when the filter is empty. |
64+
| `createItemLabel` | Optional | `string` | `ra.action .create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty. |
6565
| `debounce` | Optional | `number` | `250` | The delay to wait before calling the setFilter function injected when used in a ReferenceInput. |
6666
| `emptyText` | Optional | `string` | `''` | The text to use for the empty element |
6767
| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element |
@@ -226,7 +226,7 @@ If you just need to ask users for a single string to create the new option, you
226226
## `createLabel`
227227

228228
When you set the `create` or `onCreate` prop, `<AutocompleteInput>` lets users create new options.
229-
You can use the `createLabel` prop to render an additional menu item at the bottom of the list, that will only appear when the input is empty, inviting users to start typing to create a new option.
229+
You can use the `createLabel` prop to render an additional (disabled) menu item at the bottom of the list, that will only appear when the input is empty, inviting users to start typing to create a new option.
230230

231231
![Create Label](./img/AutocompleteInput-createLabel.png)
232232

docs/CanAccess.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,59 @@ const UserEdit = () => (
4242
| `accessDenied` | | `ReactNode` | - | The element displayed when users are denied access to the resource |
4343
| `error` | | `ReactNode` | - | The element displayed when an error occurs while calling `authProvider.canAccess` |
4444

45+
## Securing Custom Routes
46+
47+
By default, there is no authentication or authorization control on custom routes. If you need to restrict access to a custom route, wrap the content with `<CanAccess>`. Remember to check the authentication status before with `<Authenticated>`:
48+
49+
```tsx
50+
import { Authenticated, CanAccess, AccessDenied } from 'react-admin';
51+
52+
export const LogsPage = () => (
53+
<Authenticated>
54+
<CanAccess resource="logs" action="read" accessDenied={<AccessDenied />}>
55+
...
56+
</CanAccess>
57+
</Authenticated>
58+
);
59+
```
60+
61+
Use the [`<CustomRoutes>`](./CustomRoutes.md) component to add custom routes to your admin.
62+
63+
```tsx
64+
import { Admin, CustomRoutes, Authenticated, CanAccess, AccessDenied, Layout } from 'react-admin';
65+
import { Route } from 'react-router-dom';
66+
67+
import { LogsPage } from './LogsPage';
68+
import { MyMenu } from './MyMenu';
69+
70+
const MyLayout = (props) => <Layout {...props} menu={MyMenu} />;
71+
72+
const App = () => (
73+
<Admin authProvider={authProvider} layout={MyLayout}>
74+
<CustomRoutes>
75+
<Route path="/logs" element={<LogsPage />} />
76+
</CustomRoutes>
77+
</Admin>
78+
);
79+
```
80+
81+
Remember to also wrap your [custom menu items](./Menu.md) with `<CanAccess>` to hide the menu items if the user doesn't have access to the resource.
82+
83+
```tsx
84+
import { Menu, CanAccess } from "react-admin";
85+
import SsidChartIcon from "@mui/icons-material/SsidChart";
86+
87+
export const MyMenu = () => (
88+
<Menu>
89+
<Menu.ResourceItems />
90+
<CanAccess resource="logs" action="read">
91+
<Menu.Item primaryText="Logs" to="/logs" leftIcon={<SsidChartIcon />} />
92+
</CanAccess>
93+
</Menu>
94+
);
95+
```
96+
97+
**Note**: You don't need to use `<CanAccess>` on the core react-admin page components (`<List>`, `<Create>`, `<Edit>`, `<Show>`) because they already have built-in access control.
98+
99+
**Note**: You don't need to use `<Authenticated>` on custom pages if your admin uses [`requireAuth`](./Admin.md#requireauth).
45100

docs/DataProviderList.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ If you can't find a Data Provider for your backend below, no worries! [Writing a
2020
* ![corebos Logo](./img/backend-logos/corebos.png "corebos Logo")**[coreBOS](https://corebos.com/)**: [React-Admin coreBOS Integration](https://github.com/coreBOS/reactadminportal)
2121
* ![directus Logo](./img/backend-logos/directus.svg "directus Logo")**[Directus](https://directus.io/)**: [marmelab/ra-directus](https://github.com/marmelab/ra-directus/blob/main/packages/ra-directus/Readme.md)
2222
* ![django Logo](./img/backend-logos/django.png "django Logo")**[Django Rest Framework](https://www.django-rest-framework.org/)**: [bmihelac/ra-data-django-rest-framework](https://github.com/bmihelac/ra-data-django-rest-framework)
23+
* ![Eicrud Logo](./img/backend-logos/eicrud.svg "EiCrud Logo")**[Eicrud](https://github.com/eicrud/eicrud)**: [danyalutsevich/ra-data-eicrud](https://github.com/danyalutsevich/ra-data-eicrud)
2324
* ![eve Logo](./img/backend-logos/eve.png "eve Logo")**[Eve](https://docs.python-eve.org/en/stable/)**: [smeng9/ra-data-eve](https://github.com/smeng9/ra-data-eve)
2425
* ![Express Mangoose Logo](./img/backend-logos/github.svg "Express Mangoose Logo")**[Express & Mongoose](https://github.com/NathanAdhitya/express-mongoose-ra-json-server)**: [NathanAdhitya/express-mongoose-ra-json-server](https://github.com/NathanAdhitya/express-mongoose-ra-json-server)
2526
* ![Express Sequelize Logo](./img/backend-logos/github.svg "Express Sequelize Logo")**[Express & Sequelize](https://github.com/lalalilo/express-sequelize-crud)**: [express-sequelize-crud](https://github.com/lalalilo/express-sequelize-crud)

0 commit comments

Comments
 (0)