diff --git a/docs/Admin.md b/docs/Admin.md index 6a7bc3a513b..0151b08f3a8 100644 --- a/docs/Admin.md +++ b/docs/Admin.md @@ -162,36 +162,39 @@ If you want to add or remove menu items, for instance to link to non-resources p // in src/Menu.js import React, { createElement } from 'react'; import { connect } from 'react-redux'; +import { useMediaQuery } from '@material-ui/core'; import { MenuItemLink, getResources } from 'react-admin'; import { withRouter } from 'react-router-dom'; import LabelIcon from '@material-ui/icons/Label'; -import Responsive from '../layout/Responsive'; - -const Menu = ({ resources, onMenuClick, logout }) => ( -
- {resources.map(resource => ( +const Menu = ({ resources, onMenuClick, open, logout }) => { + const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); + return ( +
+ {resources.map(resource => ( + + ))} - ))} - - -
-); + {isXSmall && logout} +
+ ); +} const mapStateToProps = state => ({ + open: state.admin.ui.sidebarOpen, resources: getResources(state), }); diff --git a/docs/Authentication.md b/docs/Authentication.md index ef020f55318..e22ea196517 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -300,37 +300,25 @@ const MyLoginPage = ({ theme }) => { export default MyLoginPage; // in src/MyLogoutButton.js -import React from 'react'; +import React, { forwardRef } from 'react'; import { useDispatch } from 'react-redux'; -import { Responsive, userLogout } from 'react-admin'; +import { userLogout } from 'react-admin'; import MenuItem from '@material-ui/core/MenuItem'; -import Button from '@material-ui/core/Button'; import ExitIcon from '@material-ui/icons/PowerSettingsNew'; -const MyLogoutButton = props => { +const MyLogoutButton = forwardRef((props, ref) => { const dispatch = useDispatch(); + const logout = () => dispatch(userLogout(redirectTo)); return ( - dispatch(userLogout())} - {...props} - > - Logout - - } - medium={ - - } - /> + + Logout + ); -}; +}); + export default MyLogoutButton; // in src/App.js diff --git a/docs/List.md b/docs/List.md index a1d52b630a8..a8a34b1ecb5 100644 --- a/docs/List.md +++ b/docs/List.md @@ -1048,31 +1048,32 @@ export const PostList = (props) => ( `` iterates over the list data. For each record, it executes the `primaryText`, `secondaryText`, `leftAvatar`, `leftIcon`, `rightAvatar`, and `rightIcon` props function, and passes the result as the corresponding `` prop. -**Tip**: To use a `` on small screens and a `` on larger screens, use the `` component: +**Tip**: To use a `` on small screens and a `` on larger screens, use material-ui's `useMediaQuery` hook: ```jsx // in src/posts.js import React from 'react'; -import { List, Responsive, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; +import { useMediaQuery } from '@material-ui/core'; +import { List, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; -export const PostList = (props) => ( - - { + const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); + return ( + + {isSmall ? ( record.title} secondaryText={record => `${record.views} views`} tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> - } - medium={ + ) : ( ... - } - /> - -); + )} + + ); +} ``` **Tip**: The `` items link to the edition page by default. You can set the `linkType` prop to `show` to link to the `` page instead. @@ -1281,21 +1282,21 @@ const UserFilter = ({ permissions, ...props }) => {permissions === 'admin' ? : null} ; -export const UserList = ({ permissions, ...props }) => - } - sort={{ field: 'name', order: 'ASC' }} - > - { + const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); + return ( + } + sort={{ field: 'name', order: 'ASC' }} + > + {isSmall ? ( record.name} secondaryText={record => permissions === 'admin' ? record.role : null} /> - } - medium={ + ): ( @@ -1303,9 +1304,10 @@ export const UserList = ({ permissions, ...props }) => {permissions === 'admin' && } - } - /> - ; + )} + ; + ) +} ``` {% endraw %} diff --git a/docs/Reference.md b/docs/Reference.md index b718c69b1c7..83307aef8e5 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -68,7 +68,6 @@ title: "Reference" * [``](./Fields.md#referencemanyfield) * `` * [``](./Resource.md#the-resource-component) -* [``](./Theming.md#responsive-utility) * [``](./Fields.md#richtextfield) * [``](./Inputs.md#richtextinput) * `` diff --git a/docs/Theming.md b/docs/Theming.md index 39bb2fbb9af..e953bf6d5dc 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -221,28 +221,43 @@ export const PostList = (props) => ( If you want to read more about higher-order components, check out this SitePoint tutorial: [Higher Order Components: A React Application Design Pattern](https://www.sitepoint.com/react-higher-order-components/) -## Responsive Utility +## useMediaQuery Hook -To provide an optimized experience on mobile, tablet, and desktop devices, you often need to display different components depending on the screen size. That's the purpose of the `` component, which offers a declarative approach to responsive web design. +To provide an optimized experience on mobile, tablet, and desktop devices, you often need to display different components depending on the screen size. Material-ui provides a hook dedicated to help such responsive layouts: [`useMediaQuery`](https://material-ui.com/components/use-media-query/#usemediaquery). -It expects element props named `small`, `medium`, and `large`. It displays the element that matches the screen size (with breakpoints at 768 and 992 pixels): +It expects a function receiving the material-ui theme as a parameter, and returning a media query. Use the theme breakpoints to check for common screen sizes. The hook returns a boolean indicating if the current screen matches the media query or not. + +```jsx +const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); +const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); +const isDesktop = useMediaQuery(theme => theme.breakpoints.up('md')); +``` + +You can also pass a custom media query as a screen. + +```jsx +const isSmall = useMediaQuery('(min-width:600px)'); +``` + +Here is an example for a responsive list of posts, displaying a `SimpleList` on mobile, and a `Datagrid` otherwise: ```jsx // in src/posts.js import React from 'react'; -import { List, Responsive, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; - -export const PostList = (props) => ( - - { + const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); + return ( + + {isSmall ? ( record.title} secondaryText={record => `${record.views} views`} tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> - } - medium={ + ) : ( @@ -252,17 +267,13 @@ export const PostList = (props) => ( - } - /> - -); + )} + + ); +} ``` -**Tip**: If you only provide `small` and `medium`, the `medium` element will also be used on large screens. The same kind of smart default exists for when you omit `small` or `medium`. - -**Tip**: You can specify `null` as the value for `small`, `medium` or `large` to avoid rendering something on a specific size without falling back to others. - -**Tip**: You can also use [material-ui's `withWidth()` higher order component](https://github.com/callemall/material-ui/blob/master/src/utils/withWidth.js) to have the `with` prop injected in your own components. +**Tip**: Previous versions of react-admin shipped a `` component to do media queries. This component us now deprecated. Use `useMediaQuery` instead. ## Using a Predefined Theme @@ -659,37 +670,46 @@ By default, React-admin uses the list of `` components passed as child If you want to add or remove menu items, for instance to link to non-resources pages, you can create your own menu component: ```jsx -// in src/MyMenu.js -import React from 'react'; +// in src/Menu.js +import React, { createElement } from 'react'; import { connect } from 'react-redux'; -import { MenuItemLink, getResources, Responsive } from 'react-admin'; +import { useMediaQuery } from '@material-ui/core'; +import { MenuItemLink, getResources } from 'react-admin'; import { withRouter } from 'react-router-dom'; +import LabelIcon from '@material-ui/icons/Label'; -const MyMenu = ({ resources, onMenuClick, logout }) => ( -
- {resources.map(resource => ( +const Menu = ({ resources, onMenuClick, open, logout }) => { + const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); + return ( +
+ {resources.map(resource => ( + + ))} - ))} - - -
-); + {isXSmall && logout} +
+ ); +} const mapStateToProps = state => ({ + open: state.admin.ui.sidebarOpen, resources: getResources(state), }); -export default withRouter(connect(mapStateToProps)(MyMenu)); - +export default withRouter(connect(mapStateToProps)(Menu)); ``` **Tip**: Note the `MenuItemLink` component. It must be used to avoid unwanted side effects in mobile views. @@ -742,44 +762,46 @@ The `MenuItemLink` component make use of the React Router [`NavLink`](https://re If the default active style does not suit your tastes, you can override it by passing your own `classes`: ```jsx -// in src/MyMenu.js -import React from 'react'; +// in src/Menu.js +import React, { createElement } from 'react'; import { connect } from 'react-redux'; -import { MenuItemLink, getResources, Responsive } from 'react-admin'; -import { withStyles } from '@material-ui/core/styles'; +import { useMediaQuery } from '@material-ui/core'; +import { MenuItemLink, getResources } from 'react-admin'; import { withRouter } from 'react-router-dom'; +import LabelIcon from '@material-ui/icons/Label'; -const styles = { - root: {}, // Style applied to the MenuItem from material-ui - active: { fontWeight: 'bold' }, // Style applied when the menu item is the active one - icon: {}, // Style applied to the icon -}; - -const MyMenu = ({ classes, resources, onMenuClick, logout }) => ( -
- {resources.map(resource => ( +const Menu = ({ resources, onMenuClick, open, logout }) => { + const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); + return ( +
+ {resources.map(resource => ( + + ))} - ))} - - -
-); + {isXSmall && logout} +
+ ); +} const mapStateToProps = state => ({ + open: state.admin.ui.sidebarOpen, resources: getResources(state), }); -export default withRouter(connect(mapStateToProps)(withStyles(styles)(Menu))); +export default withRouter(connect(mapStateToProps)(Menu)); ``` ## Using a Custom Login Page diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 38bd35c9cfe..470d79d28e6 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -741,24 +741,25 @@ The `` component uses [material-ui's `` and `` compo **Note:** Since JSONRestServer doesn't provide `views` or `published_at` values for posts, we switched to a custom API for those screenshots in order to demonstrate how to use some of the `SimpleList` component props. -That works fine on mobile, but now the desktop user experience is worse. The best compromise would be to use `` on small screens, and `` on other screens. That's where the `` component comes in: +That works fine on mobile, but now the desktop user experience is worse. The best compromise would be to use `` on small screens, and `` on other screens. That's where the `useMediaQuery` hook comes in: ```jsx // in src/posts.js import React from 'react'; -import { List, Responsive, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; +import { useMediaQuery } from '@material-ui/core'; +import { List, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; -export const PostList = (props) => ( - - { + const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); + return ( + + {isSmall ? ( record.title} secondaryText={record => `${record.views} views`} tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> - } - medium={ + ) : ( @@ -768,13 +769,13 @@ export const PostList = (props) => ( - } - /> - -); + )} + + ); +} ``` -This works exactly the way you expect. The lesson here is that react-admin takes care of responsive web design for the layout, but it's your job to use `` in pages. +This works exactly the way you expect. The lesson here is that react-admin takes care of responsive web design for the layout, but it's your job to use `useMediaQuery()` in pages. ![Responsive List](./img/responsive-list.gif) diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index c7d88f8f968..12bedbfcdb7 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -693,7 +693,7 @@ Conditional Formatting
  • - Responsive Utility + useMediaQuery() Hook
  • Using a Predefined Theme