Skip to content

Commit cec6894

Browse files
committed
Merge branch 'master' into next
2 parents 15b54dc + bf568d2 commit cec6894

Some content is hidden

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

49 files changed

+768
-199
lines changed

CHANGELOG.md

+23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
# Changelog
22

3+
## v3.16.6
4+
5+
* Fix `<Empty>` component isn't properly exported ([6419](https://github.com/marmelab/react-admin/pull/6419)) ([djhi](https://github.com/djhi))
6+
7+
## v3.16.5
8+
9+
* Fix "Deprecated findDOMNode" warning in StrictMode ([6398](https://github.com/marmelab/react-admin/pull/6398)) ([fzaninotto](https://github.com/fzaninotto))
10+
* Fix `<DateTimeInput>` does not include timezone for initialValue ([6401](https://github.com/marmelab/react-admin/pull/6401)) ([djhi](https://github.com/djhi))
11+
* Fix `<TranslatableInputs>` ignores child input label ([6415](https://github.com/marmelab/react-admin/pull/6415)) ([mjomble](https://github.com/mjomble))
12+
* Fix `<Empty>` component isn't exported ([6416](https://github.com/marmelab/react-admin/pull/6416)) ([djhi](https://github.com/djhi))
13+
* [Demo] Improve dataProvider logging in GraphQL demo ([6405](https://github.com/marmelab/react-admin/pull/6405)) ([fzaninotto](https://github.com/fzaninotto))
14+
* [Doc] Add mention of `<RichTextInput>` display bug and userland fix ([6403](https://github.com/marmelab/react-admin/pull/6403)) ([fzaninotto](https://github.com/fzaninotto))
15+
16+
## v3.16.4
17+
18+
* [Demo] Optimize data loading in e-commerce demo ([6392](https://github.com/marmelab/react-admin/pull/6392)) ([djhi](https://github.com/djhi))
19+
* [Demo] Fix CRM demo points to bad source file ([6389](https://github.com/marmelab/react-admin/pull/6389)) ([fzaninotto](https://github.com/fzaninotto))
20+
* [Doc] Fix a typo in main Readme ([6390](https://github.com/marmelab/react-admin/pull/6390)) ([aminetakha](https://github.com/aminetakha))
21+
* [Doc] Fix incomplete side effect hooks documentation ([6388](https://github.com/marmelab/react-admin/pull/6388)) ([fzaninotto](https://github.com/fzaninotto))
22+
* [Doc] Fix misleading explanation of `<List syncWithLocation>` prop ([6385](https://github.com/marmelab/react-admin/pull/6385)) ([fzaninotto](https://github.com/fzaninotto))
23+
* [Doc] Fix `<ListBase>` snippet doesn't explain how to override the title ([6383](https://github.com/marmelab/react-admin/pull/6383)) ([fzaninotto](https://github.com/fzaninotto))
24+
* [Doc] Fix wrong ending tags in Actions documentation ([6382](https://github.com/marmelab/react-admin/pull/6382)) ([Cornul11](https://github.com/Cornul11))
25+
326
## v3.16.3
427

528
* Fix `useInput` incorrectly sets default value for numbers ([6374](https://github.com/marmelab/react-admin/pull/6374)) ([djhi](https://github.com/djhi))

docs/Actions.md

+150-8
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ const IncreaseLikeButton = ({ record }) => {
449449
update('likes', record.id, diff, record)
450450
}
451451
if (error) { return <p>ERROR</p>; }
452-
return <button disabled={loading} onClick={handleClick}>Like</div>;
452+
return <button disabled={loading} onClick={handleClick}>Like</button>;
453453
};
454454

455455
// or set params when calling the hook
@@ -459,7 +459,7 @@ const IncreaseLikeButton = ({ record }) => {
459459
const diff = { likes: record.likes + 1 };
460460
const [update, { loading, error }] = useUpdate('likes', record.id, diff, record);
461461
if (error) { return <p>ERROR</p>; }
462-
return <button disabled={loading} onClick={update}>Like</div>;
462+
return <button disabled={loading} onClick={update}>Like</button>;
463463
};
464464
```
465465

@@ -520,7 +520,7 @@ const DeleteButton = ({ record }) => {
520520
deleteOne('likes', record.id, record)
521521
}
522522
if (error) { return <p>ERROR</p>; }
523-
return <button disabled={loading} onClick={handleClick}>Delete</div>;
523+
return <button disabled={loading} onClick={handleClick}>Delete</button>;
524524
};
525525

526526
// set params when calling the hook
@@ -529,7 +529,7 @@ import { useDelete } from 'react-admin';
529529
const DeleteButton = ({ record }) => {
530530
const [deleteOne, { loading, error }] = useDelete('likes', record.id, record);
531531
if (error) { return <p>ERROR</p>; }
532-
return <button disabled={loading} onClick={deleteOne}>Delete</div>;
532+
return <button disabled={loading} onClick={deleteOne}>Delete</button>;
533533
};
534534
```
535535

@@ -620,10 +620,152 @@ const ApproveButton = ({ record }) => {
620620

621621
Fetching data is called a *side effect*, since it calls the outside world, and is asynchronous. Usual actions may have other side effects, like showing a notification, or redirecting the user to another page. React-admin provides the following hooks to handle most common side effects:
622622

623-
- `useNotify`: Return a function to display a notification. The arguments should be a message (it can be a translation key), a level (either `info`, `success` or `warning`), an `options` object to pass to the `translate` function (in the case of the default i18n provider, using Polyglot.js, it will be the interpolation options used for passing variables), a boolean to set to `true` if the notification should contain an "undo" button and a number corresponding to the notification duration.
624-
- `useRedirect`: Return a function to redirect the user to another page. The arguments should be the path to redirect the user to, and the current `basePath`.
625-
- `useRefresh`: Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
626-
- `useUnselectAll`: Return a function to unselect all lines in the current `Datagrid`. Pass the name of the resource as argument.
623+
- [`useNotify`](#usenotify): Return a function to display a notification.
624+
- [`useRedirect`](#useredirect): Return a function to redirect the user to another page.
625+
- [`useRefresh`](#userefresh): Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
626+
- [`useUnselectAll`](#useunselectall): Return a function to unselect all lines in the current `Datagrid`.
627+
628+
### `useNotify`
629+
630+
This hook returns a function that displays a notification in the bottom of the page.
631+
632+
```jsx
633+
import { useNotify } from 'react-admin';
634+
635+
const NotifyButton = () => {
636+
const notify = useNotify();
637+
const handleClick = () => {
638+
notify(`Comment approved`, 'success');
639+
}
640+
return <button onClick={handleClick}>Notify</button>;
641+
};
642+
```
643+
644+
The callback takes 5 arguments:
645+
- the message to display
646+
- the level of the notification (`info`, `success` or `warning` - the default is `info`)
647+
- an `options` object to pass to the `translate` function (because notificatoin messages are translated if your admin has an `i18nProvider`). It is useful for inserting variables into the translation.
648+
- an `undoable` boolean. Set it to `true` if the notification should contain an "undo" button
649+
- a `duration` number. Set it to `0` if the notification should not be dismissable.
650+
651+
Here are more examples of `useNotify` calls:
652+
653+
```jsx
654+
// notify a warning
655+
notify(`This is a warning`, 'warning');
656+
// pass translation arguments
657+
notify('item.created', 'info', { resource: 'post' });
658+
// send an undoable notification
659+
notify('Element updated', 'info', undefined, true);
660+
```
661+
662+
**Tip**: When using `useNotify` as a side effect for an `undoable` Edit form, you MUST set the fourth argument to `true`, otherwise the "undo" button will not appear, and the actual update will never occur.
663+
664+
```jsx
665+
import * as React from 'react';
666+
import { useNotify, Edit, SimpleForm } from 'react-admin';
667+
668+
const PostEdit = props => {
669+
const notify = useNotify();
670+
671+
const onSuccess = () => {
672+
notify(`Changes saved`, undefined, undefined, true);
673+
};
674+
675+
return (
676+
<Edit undoable onSuccess={onSuccess} {...props}>
677+
<SimpleForm>
678+
...
679+
</SimpleForm>
680+
</Edit>
681+
);
682+
}
683+
```
684+
685+
### `useRedirect`
686+
687+
This hook returns a function that redirects the user to another page.
688+
689+
```jsx
690+
import { useRedirect } from 'react-admin';
691+
692+
const DashboardButton = () => {
693+
const redirect = useRedirect();
694+
const handleClick = () => {
695+
redirect('/dashboard');
696+
}
697+
return <button onClick={handleClick}>Dashboard</button>;
698+
};
699+
```
700+
701+
The callback takes 3 arguments:
702+
- the page to redirect the user to ('list', 'create', 'edit', 'show', or a custom path)
703+
- the current `basePath`
704+
- the `id` of the record to redirect to (if any)
705+
706+
Here are more examples of `useRedirect` calls:
707+
708+
```jsx
709+
// redirect to the post list page
710+
redirect('list', '/posts');
711+
// redirect to the edit page of a post:
712+
redirect('edit', '/posts', 1);
713+
// redirect to the post creation page:
714+
redirect('create', '/posts');
715+
```
716+
717+
Note that `useRedirect` doesn't allow to redirect to pages outside the current React app. For that, you should use `document.location`.
718+
719+
### `useRefresh`
720+
721+
This hook returns a function that forces a rerender of the current view.
722+
723+
```jsx
724+
import { useRefresh } from 'react-admin';
725+
726+
const RefreshButton = () => {
727+
const refresh = useRefresh();
728+
const handleClick = () => {
729+
refresh();
730+
}
731+
return <button onClick={handleClick}>Refresh</button>;
732+
};
733+
```
734+
735+
To make this work, react-admin stores a `version` number in its state. The `useDataProvider()` hook uses this `version` in its effect dependencies. Also, page components use the `version` as `key`. The `refresh` callback increases the `version`, which forces a re-execution all queries based on the `useDataProvider()` hook, and a rerender of all components using the `version` as key.
736+
737+
This means that you can make any component inside a react-admin app refreshable by using the right key:
738+
739+
```jsx
740+
import * as React from 'react';
741+
import { useVersion } from 'react-admin';
742+
743+
const MyComponent = () => {
744+
const version = useVersion();
745+
return <div key={version}>
746+
...
747+
</div>;
748+
};
749+
```
750+
751+
The callback takes 1 argument:
752+
- `hard`: when set to true, the callback empties the cache, too
753+
754+
### `useUnselectAll`
755+
756+
This hook returns a function that unselects all lines in the current `Datagrid`. Pass the name of the resource as argument.
757+
758+
```jsx
759+
import { useUnselectAll } from 'react-admin';
760+
761+
const UnselectAllButton = () => {
762+
const unselectAll = useUnselectAll();
763+
const handleClick = () => {
764+
unselectAll('posts');
765+
}
766+
return <button onClick={handleClick}>Unselect all</button>;
767+
};
768+
```
627769

628770
## Handling Side Effects In Other Hooks
629771

docs/Buttons.md

+65
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,71 @@ To override the style of all instances of `<SkipNavigationButton>` using the [ma
345345

346346
### `<MenuItemLink>`
347347

348+
The `<MenuItemLink>` component displays a menu item with a label and an icon - or only the icon with a tooltip when the sidebar is minimized. It also handles the automatic closing of the menu on tap on mobile.
349+
350+
![custom menu icons](./img/custom-menu.gif)
351+
352+
| Prop | Required | Type | Default | Description |
353+
| ------------- | -------- | -------------------- | ------- | ---------------------------------------- |
354+
| `to` | Required | `string | location` | - | The menu item's target. It is passed to a React Router [NavLink](https://reacttraining.com/react-router/web/api/NavLink) component. |
355+
| `primaryText` | Required | `string | ReactNode` | - | The menu content, displayed when the menu isn't minimized. |
356+
| `leftIcon` | Optional | `ReactNode` | - | The menu icon |
357+
358+
Additional props are passed down to [the underling material-ui `<MenuItem>` component](https://material-ui.com/api/menu-item/#menuitem-api).
359+
360+
You can create a custom menu component using the `<DashboardMenuItem>` and `<MenuItemLink>` components:
361+
362+
```jsx
363+
// in src/Menu.js
364+
import * as React from 'react';
365+
import { DashboardMenuItem, MenuItemLink } from 'react-admin';
366+
import BookIcon from '@material-ui/icons/Book';
367+
import ChatBubbleIcon from '@material-ui/icons/ChatBubble';
368+
import PeopleIcon from '@material-ui/icons/People';
369+
import LabelIcon from '@material-ui/icons/Label';
370+
371+
export const Menu = () => (
372+
<div>
373+
<DashboardMenuItem />
374+
<MenuItemLink to="/posts" primaryText="Posts" leftIcon={<BookIcon />}/>
375+
<MenuItemLink to="/comments" primaryText="Comments" leftIcon={<ChatBubbleIcon />}/>
376+
<MenuItemLink to="/users" primaryText="Users" leftIcon={<PeopleIcon />}/>
377+
<MenuItemLink to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
378+
</div>
379+
);
380+
```
381+
382+
To use this custom menu component, pass it to a custom Layout:
383+
384+
```jsx
385+
// in src/Layout.js
386+
import { Layout } from 'react-admin';
387+
import { Menu } from './Menu';
388+
389+
export const Layout = (props) => <Layout {...props} menu={Menu} />;
390+
```
391+
392+
Then, use this layout in the `<Admin>` `layout` prop:
393+
394+
```jsx
395+
// in src/App.js
396+
import { Layout } from './Layout';
397+
398+
const App = () => (
399+
<Admin layout={Layout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
400+
// ...
401+
</Admin>
402+
);
403+
```
404+
405+
See [The theming documentation](./Theming.md#menuitemlink) for more details.
406+
407+
**Tip**: If you need a multi-level menu, or a Mega Menu opening panels with custom content, check out [the `ra-navigation`<img class="icon" src="./img/premium.svg" /> module](https://marmelab.com/ra-enterprise/modules/ra-navigation) (part of the [Enterprise Edition](https://marmelab.com/ra-enterprise))
408+
409+
![multi-level menu](https://marmelab.com/ra-enterprise/modules/assets/ra-multilevelmenu-item.gif)
410+
411+
![MegaMenu and Breadcrumb](https://marmelab.com/ra-enterprise/modules/assets/ra-multilevelmenu-categories.gif)
412+
348413
#### CSS API
349414

350415
| Rule name | Description |

docs/DataProviders.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ Developers from the react-admin community have open-sourced Data Providers for m
9393
* **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer)
9494
* **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider)
9595
* **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud)
96+
* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server)
97+
* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud)
9698
* **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse)
9799
* **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile)
98100
* **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest)
99101
* **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma)
100102
* **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma)
101103
* **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3)
102-
* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server)
103-
* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud)
104104
* **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi)
105105
* **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails)
106106
* **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest)

docs/Inputs.md

+19
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,25 @@ const configureQuill = quill => quill.getModule('toolbar').addHandler('bold', fu
452452

453453
`<RichTextInput>` also accepts the [common input props](./Inputs.md#common-input-props).
454454

455+
**Tip**: When used inside a material-ui `<Card>` (e.g in the default `<Edit>` view), `<RichTextInput>` displays link tooltip as cut off when the user wants to add a hyperlink to a word located on the left side of the input. This is due to an incompatibility between material-ui's `<Card>` component and Quill's positioning algorithm for the link tooltip.
456+
457+
To fix this problem, you should override the default card style, as follows:
458+
459+
```diff
460+
import { Edit, SimpleForm, TextInput } from 'react-admin';
461+
+import { withStyles } from '@material-ui/core/styles';
462+
463+
-const PostEdit = props => (
464+
+const PostEdit = withStyles({ card: { overflow: 'initial' } })(props => (
465+
<Edit {...props}>
466+
<SimpleForm>
467+
// ...
468+
</SimpleForm>
469+
</Edit>
470+
-);
471+
+));
472+
```
473+
455474
### `<TextInput>`
456475

457476
`<TextInput>` is the most common input. It is used for texts, emails, URL or passwords. In translates to an HTML `<input>` tag.

0 commit comments

Comments
 (0)