Skip to content

Commit

Permalink
Merge branch 'master' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Jul 8, 2021
2 parents 15b54dc + bf568d2 commit cec6894
Show file tree
Hide file tree
Showing 49 changed files with 768 additions and 199 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## v3.16.6

* Fix `<Empty>` component isn't properly exported ([6419](https://github.com/marmelab/react-admin/pull/6419)) ([djhi](https://github.com/djhi))

## v3.16.5

* Fix "Deprecated findDOMNode" warning in StrictMode ([6398](https://github.com/marmelab/react-admin/pull/6398)) ([fzaninotto](https://github.com/fzaninotto))
* Fix `<DateTimeInput>` does not include timezone for initialValue ([6401](https://github.com/marmelab/react-admin/pull/6401)) ([djhi](https://github.com/djhi))
* Fix `<TranslatableInputs>` ignores child input label ([6415](https://github.com/marmelab/react-admin/pull/6415)) ([mjomble](https://github.com/mjomble))
* Fix `<Empty>` component isn't exported ([6416](https://github.com/marmelab/react-admin/pull/6416)) ([djhi](https://github.com/djhi))
* [Demo] Improve dataProvider logging in GraphQL demo ([6405](https://github.com/marmelab/react-admin/pull/6405)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Add mention of `<RichTextInput>` display bug and userland fix ([6403](https://github.com/marmelab/react-admin/pull/6403)) ([fzaninotto](https://github.com/fzaninotto))

## v3.16.4

* [Demo] Optimize data loading in e-commerce demo ([6392](https://github.com/marmelab/react-admin/pull/6392)) ([djhi](https://github.com/djhi))
* [Demo] Fix CRM demo points to bad source file ([6389](https://github.com/marmelab/react-admin/pull/6389)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Fix a typo in main Readme ([6390](https://github.com/marmelab/react-admin/pull/6390)) ([aminetakha](https://github.com/aminetakha))
* [Doc] Fix incomplete side effect hooks documentation ([6388](https://github.com/marmelab/react-admin/pull/6388)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Fix misleading explanation of `<List syncWithLocation>` prop ([6385](https://github.com/marmelab/react-admin/pull/6385)) ([fzaninotto](https://github.com/fzaninotto))
* [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))
* [Doc] Fix wrong ending tags in Actions documentation ([6382](https://github.com/marmelab/react-admin/pull/6382)) ([Cornul11](https://github.com/Cornul11))

## v3.16.3

* Fix `useInput` incorrectly sets default value for numbers ([6374](https://github.com/marmelab/react-admin/pull/6374)) ([djhi](https://github.com/djhi))
Expand Down
158 changes: 150 additions & 8 deletions docs/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ const IncreaseLikeButton = ({ record }) => {
update('likes', record.id, diff, record)
}
if (error) { return <p>ERROR</p>; }
return <button disabled={loading} onClick={handleClick}>Like</div>;
return <button disabled={loading} onClick={handleClick}>Like</button>;
};

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

Expand Down Expand Up @@ -520,7 +520,7 @@ const DeleteButton = ({ record }) => {
deleteOne('likes', record.id, record)
}
if (error) { return <p>ERROR</p>; }
return <button disabled={loading} onClick={handleClick}>Delete</div>;
return <button disabled={loading} onClick={handleClick}>Delete</button>;
};

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

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

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:

- `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.
- `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`.
- `useRefresh`: Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
- `useUnselectAll`: Return a function to unselect all lines in the current `Datagrid`. Pass the name of the resource as argument.
- [`useNotify`](#usenotify): Return a function to display a notification.
- [`useRedirect`](#useredirect): Return a function to redirect the user to another page.
- [`useRefresh`](#userefresh): Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
- [`useUnselectAll`](#useunselectall): Return a function to unselect all lines in the current `Datagrid`.

### `useNotify`

This hook returns a function that displays a notification in the bottom of the page.

```jsx
import { useNotify } from 'react-admin';

const NotifyButton = () => {
const notify = useNotify();
const handleClick = () => {
notify(`Comment approved`, 'success');
}
return <button onClick={handleClick}>Notify</button>;
};
```

The callback takes 5 arguments:
- the message to display
- the level of the notification (`info`, `success` or `warning` - the default is `info`)
- 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.
- an `undoable` boolean. Set it to `true` if the notification should contain an "undo" button
- a `duration` number. Set it to `0` if the notification should not be dismissable.

Here are more examples of `useNotify` calls:

```jsx
// notify a warning
notify(`This is a warning`, 'warning');
// pass translation arguments
notify('item.created', 'info', { resource: 'post' });
// send an undoable notification
notify('Element updated', 'info', undefined, true);
```

**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.

```jsx
import * as React from 'react';
import { useNotify, Edit, SimpleForm } from 'react-admin';

const PostEdit = props => {
const notify = useNotify();

const onSuccess = () => {
notify(`Changes saved`, undefined, undefined, true);
};

return (
<Edit undoable onSuccess={onSuccess} {...props}>
<SimpleForm>
...
</SimpleForm>
</Edit>
);
}
```

### `useRedirect`

This hook returns a function that redirects the user to another page.

```jsx
import { useRedirect } from 'react-admin';

const DashboardButton = () => {
const redirect = useRedirect();
const handleClick = () => {
redirect('/dashboard');
}
return <button onClick={handleClick}>Dashboard</button>;
};
```

The callback takes 3 arguments:
- the page to redirect the user to ('list', 'create', 'edit', 'show', or a custom path)
- the current `basePath`
- the `id` of the record to redirect to (if any)

Here are more examples of `useRedirect` calls:

```jsx
// redirect to the post list page
redirect('list', '/posts');
// redirect to the edit page of a post:
redirect('edit', '/posts', 1);
// redirect to the post creation page:
redirect('create', '/posts');
```

Note that `useRedirect` doesn't allow to redirect to pages outside the current React app. For that, you should use `document.location`.

### `useRefresh`

This hook returns a function that forces a rerender of the current view.

```jsx
import { useRefresh } from 'react-admin';

const RefreshButton = () => {
const refresh = useRefresh();
const handleClick = () => {
refresh();
}
return <button onClick={handleClick}>Refresh</button>;
};
```

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.

This means that you can make any component inside a react-admin app refreshable by using the right key:

```jsx
import * as React from 'react';
import { useVersion } from 'react-admin';

const MyComponent = () => {
const version = useVersion();
return <div key={version}>
...
</div>;
};
```

The callback takes 1 argument:
- `hard`: when set to true, the callback empties the cache, too

### `useUnselectAll`

This hook returns a function that unselects all lines in the current `Datagrid`. Pass the name of the resource as argument.

```jsx
import { useUnselectAll } from 'react-admin';

const UnselectAllButton = () => {
const unselectAll = useUnselectAll();
const handleClick = () => {
unselectAll('posts');
}
return <button onClick={handleClick}>Unselect all</button>;
};
```

## Handling Side Effects In Other Hooks

Expand Down
65 changes: 65 additions & 0 deletions docs/Buttons.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,71 @@ To override the style of all instances of `<SkipNavigationButton>` using the [ma

### `<MenuItemLink>`

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.

![custom menu icons](./img/custom-menu.gif)

| Prop | Required | Type | Default | Description |
| ------------- | -------- | -------------------- | ------- | ---------------------------------------- |
| `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. |
| `primaryText` | Required | `string | ReactNode` | - | The menu content, displayed when the menu isn't minimized. |
| `leftIcon` | Optional | `ReactNode` | - | The menu icon |

Additional props are passed down to [the underling material-ui `<MenuItem>` component](https://material-ui.com/api/menu-item/#menuitem-api).

You can create a custom menu component using the `<DashboardMenuItem>` and `<MenuItemLink>` components:

```jsx
// in src/Menu.js
import * as React from 'react';
import { DashboardMenuItem, MenuItemLink } from 'react-admin';
import BookIcon from '@material-ui/icons/Book';
import ChatBubbleIcon from '@material-ui/icons/ChatBubble';
import PeopleIcon from '@material-ui/icons/People';
import LabelIcon from '@material-ui/icons/Label';

export const Menu = () => (
<div>
<DashboardMenuItem />
<MenuItemLink to="/posts" primaryText="Posts" leftIcon={<BookIcon />}/>
<MenuItemLink to="/comments" primaryText="Comments" leftIcon={<ChatBubbleIcon />}/>
<MenuItemLink to="/users" primaryText="Users" leftIcon={<PeopleIcon />}/>
<MenuItemLink to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
</div>
);
```

To use this custom menu component, pass it to a custom Layout:

```jsx
// in src/Layout.js
import { Layout } from 'react-admin';
import { Menu } from './Menu';

export const Layout = (props) => <Layout {...props} menu={Menu} />;
```

Then, use this layout in the `<Admin>` `layout` prop:

```jsx
// in src/App.js
import { Layout } from './Layout';

const App = () => (
<Admin layout={Layout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
// ...
</Admin>
);
```

See [The theming documentation](./Theming.md#menuitemlink) for more details.

**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))

![multi-level menu](https://marmelab.com/ra-enterprise/modules/assets/ra-multilevelmenu-item.gif)

![MegaMenu and Breadcrumb](https://marmelab.com/ra-enterprise/modules/assets/ra-multilevelmenu-categories.gif)

#### CSS API

| Rule name | Description |
Expand Down
4 changes: 2 additions & 2 deletions docs/DataProviders.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ Developers from the react-admin community have open-sourced Data Providers for m
* **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer)
* **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider)
* **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud)
* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server)
* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud)
* **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse)
* **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile)
* **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest)
* **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma)
* **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma)
* **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3)
* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server)
* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud)
* **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi)
* **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails)
* **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest)
Expand Down
19 changes: 19 additions & 0 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,25 @@ const configureQuill = quill => quill.getModule('toolbar').addHandler('bold', fu

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

**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.

To fix this problem, you should override the default card style, as follows:

```diff
import { Edit, SimpleForm, TextInput } from 'react-admin';
+import { withStyles } from '@material-ui/core/styles';

-const PostEdit = props => (
+const PostEdit = withStyles({ card: { overflow: 'initial' } })(props => (
<Edit {...props}>
<SimpleForm>
// ...
</SimpleForm>
</Edit>
-);
+));
```

### `<TextInput>`

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

0 comments on commit cec6894

Please sign in to comment.