From f264a2feb0b2ada7906b36570b812ee637178f01 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 22 Aug 2019 17:47:40 -0300 Subject: [PATCH 1/3] Convert examples to hooks --- docs/Theming.md | 218 +++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 106 deletions(-) diff --git a/docs/Theming.md b/docs/Theming.md index 5fc2c6bb586..caccda7d2d8 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -16,22 +16,20 @@ Here is an example customizing an `EditButton` component inside a `Datagrid`, us {% raw %} ```jsx import { NumberField, List, Datagrid, EditButton } from 'react-admin'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; -const styles = { +const useStyles = makeStyles({ button: { fontWeight: 'bold', // This is JSS syntax to target a deeper element using css selector, here the svg icon for this button '& svg': { color: 'orange' } }, -}; +}); -const MyEditButton = withStyles(styles)(({ classes, ...props }) => ( - -)); +const MyEditButton = props => { + const classes = useStyles(); + return ; +}; export const ProductList = (props) => ( @@ -65,65 +63,71 @@ import { TextInput, } from 'react-admin'; import Icon from '@material-ui/icons/Person'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; export const VisitorIcon = Icon; // The Filter component supports the `form` and `button` CSS classes. Here we override the `form` class -const filterStyles = { +const useStyles = makeStyles({ form: { backgroundColor: 'Lavender', }, -}; +}); -const VisitorFilter = withStyles(filterStyles)(({ classes, ...props }) => ( - - - - - - -)); +const VisitorFilter = props => { + const classes = useStyles(); + return ( + + + + + + + ); +}; // The List component supports the `root`, `header`, `actions` and `noResults` CSS classes. Here we override the `header` and `actions` classes -const listStyles = { +const useStyles = makeStyles({ actions: { backgroundColor: 'Lavender', }, header: { backgroundColor: 'Lavender', }, -}; +}); -export const VisitorList = withStyles(listStyles)(({ classes, ...props }) => ( - } - sort={{ field: 'last_seen', order: 'DESC' }} - perPage={25} - > - - - - - - - - - -)); +export const VisitorList = props => { + const classes = useStyles(); + return ( + } + sort={{ field: 'last_seen', order: 'DESC' }} + perPage={25} + > + + + + + + + + + + ) +}; ``` {% endraw %} @@ -142,16 +146,17 @@ Sometimes you want the format to depend on the value. The following example show {% raw %} ```jsx import { NumberField, List, Datagrid, EditButton } from 'react-admin'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyle } from '@material-ui/core/styles'; import classnames from 'classnames'; -const coloredStyles = { +const useStyles = makeStyles({ small: { color: 'black' }, big: { color: 'red' }, -}; +}); -const ColoredNumberField = withStyles(coloredStyles)( - ({ classes, ...props }) => ( +const ColoredNumberField = props => { + const classes = useStyles(); + return ( - )); + ); +}; // Ensure the original component defaultProps are still applied as they may be used by its parents (such as the `Show` component): ColoredNumberField.defaultProps = NumberField.defaultProps; @@ -182,16 +188,17 @@ Furthermore, you may extract this highlighting strategy into an Higher Order Com {% raw %} ```jsx import { NumberField, List, Datagrid, EditButton } from 'react-admin'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; -const coloredStyles = { +const useStyles = makeStyles({ small: { color: 'black' }, big: { color: 'red' }, -}; +}); -const colored = WrappedComponent => withStyles(coloredStyles)( - ({ classes, ...props }) => ( +const colored = WrappedComponent => props => { + const classes = useStyles(); + return ( withStyles(coloredStyles)( })} {...props} /> - )); + ) +}; const ColoredNumberField = colored(NumberField); @@ -425,7 +433,7 @@ You can also customize the default icon by setting the `icon` prop to the ` ({ +const useStyles = makeStyles(theme => ({ root: { display: 'flex', flexDirection: 'column', @@ -516,40 +524,41 @@ const styles = theme => ({ marginTop: '4em', paddingLeft: 5, }, -}); - -class MyLayout extends Component { - componentWillMount() { - this.props.setSidebarVisibility(true); - } - - render() { - const { - children, - classes, - dashboard, - isLoading, - logout, - open, - title, - } = this.props; - return ( -
-
- -
- - - -
- {children} -
-
- -
+})); + +const MyLayout = ({ + children, + classes, + dashboard, + isLoading, + logout, + open, + title, +}) => { + const classes = useStyles(); + const dispatch = useDispatch(); + const isLoading = useSelector(state => state.admin.loading > 0); + + useEffect(() => { + dispatch(setSidebarVisibility(true)); + }, [setSidebarVisibility]); + + return ( +
+
+ +
+ + + +
+ {children} +
+
+
- ); - } +
+ ); } MyLayout.propTypes = { @@ -558,14 +567,11 @@ MyLayout.propTypes = { PropTypes.func, PropTypes.string, ]), - isLoading: PropTypes.bool.isRequired, logout: componentPropType, - setSidebarVisibility: PropTypes.func.isRequired, title: PropTypes.string.isRequired, }; -const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 }); -export default connect(mapStateToProps, { setSidebarVisibility })(withStyles(styles)(MyLayout)); +export default MyLayout ``` ## Customizing the AppBar Content @@ -579,7 +585,7 @@ Here is an example customization for `` to include a company logo in the import React from 'react'; import { AppBar } from 'react-admin'; import Typography from '@material-ui/core/Typography'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import Logo from './Logo'; From b4f33209f8d940d8e0e313a8fe93c0562a534b7c Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Fri, 23 Aug 2019 10:33:29 -0300 Subject: [PATCH 2/3] Convert examples to hooks --- docs/Theming.md | 126 ++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/docs/Theming.md b/docs/Theming.md index caccda7d2d8..f655537bb7b 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -11,7 +11,7 @@ Whether you need to adjust a CSS rule for a single component, or change the colo Every react-admin component provides a `className` property, which is always applied to the root element. -Here is an example customizing an `EditButton` component inside a `Datagrid`, using its `className` property and the `withStyles` Higher Order Component from Material-UI: +Here is an example customizing an `EditButton` component inside a `Datagrid`, using its `className` property and the `makeStyle` hook from Material-UI: {% raw %} ```jsx @@ -146,7 +146,7 @@ Sometimes you want the format to depend on the value. The following example show {% raw %} ```jsx import { NumberField, List, Datagrid, EditButton } from 'react-admin'; -import { makeStyle } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; const useStyles = makeStyles({ @@ -170,7 +170,7 @@ const ColoredNumberField = props => { // Ensure the original component defaultProps are still applied as they may be used by its parents (such as the `Show` component): ColoredNumberField.defaultProps = NumberField.defaultProps; -export const PostList = (props) => ( +export const PostList = props => ( @@ -436,21 +436,22 @@ import { AppBar, UserMenu } from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import Avatar from '@material-ui/core/Avatar'; -const myCustomIconStyle = { +const useStyles = makeStyles({ avatar: { height: 30, width: 30, }, -}; +}); -const MyCustomIcon = withStyles(myCustomIconStyle)( - ({ classes }) => ( +const MyCustomIcon = () => { + const classes = useStyles(); + return ( ) -); +}; const MyUserMenu = props => (); @@ -488,7 +489,7 @@ For more custom layouts, write a component from scratch. It must contain a `{chi import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useSelector, useDispatch } from 'react-redux'; -import { makeStyles, createMuiTheme } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import { ThemeProvider } from '@material-ui/styles'; import { AppBar, @@ -528,16 +529,13 @@ const useStyles = makeStyles(theme => ({ const MyLayout = ({ children, - classes, dashboard, - isLoading, logout, - open, title, }) => { const classes = useStyles(); const dispatch = useDispatch(); - const isLoading = useSelector(state => state.admin.loading > 0); + const open = useSelector(state => state.admin.ui.sidebarOpen); useEffect(() => { dispatch(setSidebarVisibility(true)); @@ -589,7 +587,7 @@ import { makeStyles } from '@material-ui/core/styles'; import Logo from './Logo'; -const styles = { +const useStyles = makeStyles({ title: { flex: 1, textOverflow: 'ellipsis', @@ -599,20 +597,23 @@ const styles = { spacer: { flex: 1, }, -}; +}); -const MyAppBar = withStyles(styles)(({ classes, ...props }) => ( - - - - - -)); +const MyAppBar = props => { + const classes = useStyles(); + return ( + + + + + + ); +}; export default MyAppBar; ``` @@ -678,14 +679,16 @@ If you want to add or remove menu items, for instance to link to non-resources p ```jsx // in src/Menu.js import React, { createElement } from 'react'; -import { connect } from 'react-redux'; +import { useSelector } 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'; -const Menu = ({ resources, onMenuClick, open, logout }) => { +const Menu = ({ onMenuClick, logout }) => { const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); + const open = useSelector(state => state.admin.ui.sidebarOpen); + const resources = useSelector(getResources); return (
{resources.map(resource => ( @@ -710,12 +713,7 @@ const Menu = ({ resources, onMenuClick, open, logout }) => { ); } -const mapStateToProps = state => ({ - open: state.admin.ui.sidebarOpen, - resources: getResources(state), -}); - -export default withRouter(connect(mapStateToProps)(Menu)); +export default withRouter(Menu); ``` **Tip**: Note the `MenuItemLink` component. It must be used to avoid unwanted side effects in mobile views. @@ -770,14 +768,16 @@ If the default active style does not suit your tastes, you can override it by pa ```jsx // in src/Menu.js import React, { createElement } from 'react'; -import { connect } from 'react-redux'; +import { useSelector } 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'; -const Menu = ({ resources, onMenuClick, open, logout }) => { +const Menu = ({ onMenuClick, logout }) => { const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); + const open = useSelector(state => state.admin.ui.sidebarOpen); + const resources = useSelector(getResources); return (
{resources.map(resource => ( @@ -802,12 +802,7 @@ const Menu = ({ resources, onMenuClick, open, logout }) => { ); } -const mapStateToProps = state => ({ - open: state.admin.ui.sidebarOpen, - resources: getResources(state), -}); - -export default withRouter(connect(mapStateToProps)(Menu)); +export default withRouter(Menu); ``` ## Using a Custom Login Page @@ -878,34 +873,37 @@ import React from 'react'; import Button from '@material-ui/core/Button'; import ErrorIcon from '@material-ui/icons/Report'; import History from '@material-ui/icons/History'; -import { Title } from 'react-admin'; +import { Title, useTranslate } from 'react-admin'; const MyError = ({ error, errorInfo, ...rest -}) => ( -
- - <h1><ErrorIcon /> Something Went Wrong </h1> - <div>A client error occurred and your request couldn't be completed.</div> - {process.env.NODE_ENV !== 'production' && ( - <details> - <h2>{translate(error.toString())}</h2> - {errorInfo.componentStack} - </details> - )} +}) => { + const translate = useTranslate(); + return ( <div> - <Button - variant="contained" - icon={<History />} - onClick={() => history.go(-1)} - > - Back - </Button> + <Title title="Error" /> + <h1><ErrorIcon /> Something Went Wrong </h1> + <div>A client error occurred and your request couldn't be completed.</div> + {process.env.NODE_ENV !== 'production' && ( + <details> + <h2>{translate(error.toString())}</h2> + {errorInfo.componentStack} + </details> + )} + <div> + <Button + variant="contained" + icon={<History />} + onClick={() => history.go(-1)} + > + Back + </Button> + </div> </div> - </div> -); + ); +} export default MyError; ``` From 7527dc97a260b313ea82ad2e4b5eda57d71d9358 Mon Sep 17 00:00:00 2001 From: Matheus Wichman <matheushw@outlook.com> Date: Wed, 28 Aug 2019 09:21:46 -0300 Subject: [PATCH 3/3] Fix useStyles naming --- docs/Theming.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Theming.md b/docs/Theming.md index f655537bb7b..e002bc84ebf 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -68,14 +68,14 @@ import { makeStyles } from '@material-ui/core/styles'; export const VisitorIcon = Icon; // The Filter component supports the `form` and `button` CSS classes. Here we override the `form` class -const useStyles = makeStyles({ +const useFilterStyles = makeStyles({ form: { backgroundColor: 'Lavender', }, }); const VisitorFilter = props => { - const classes = useStyles(); + const classes = useFilterStyles(); return ( <Filter classes={classes} {...props}> <TextInput @@ -92,7 +92,7 @@ const VisitorFilter = props => { }; // The List component supports the `root`, `header`, `actions` and `noResults` CSS classes. Here we override the `header` and `actions` classes -const useStyles = makeStyles({ +const useListStyles = makeStyles({ actions: { backgroundColor: 'Lavender', }, @@ -102,7 +102,7 @@ const useStyles = makeStyles({ }); export const VisitorList = props => { - const classes = useStyles(); + const classes = useListStyles(); return ( <List classes={classes}