From 1065e76ca4884db72d6d3bf16bd6bcaa9f839313 Mon Sep 17 00:00:00 2001 From: Travis CI Date: Tue, 21 Sep 2021 16:59:01 +0200 Subject: [PATCH 1/3] Extract the Sidebar ToggleButton --- docs/Theming.md | 308 +++++++++++------- .../ra-ui-materialui/src/layout/AppBar.tsx | 99 ++---- .../src/layout/SidebarToggleButton.tsx | 71 ++++ .../src/layout/useToggleSidebar.ts | 29 ++ 4 files changed, 326 insertions(+), 181 deletions(-) create mode 100644 packages/ra-ui-materialui/src/layout/SidebarToggleButton.tsx create mode 100644 packages/ra-ui-materialui/src/layout/useToggleSidebar.ts diff --git a/docs/Theming.md b/docs/Theming.md index a7c20db275d..c5724fcb125 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -1,6 +1,6 @@ --- layout: default -title: "Theming" +title: 'Theming' --- # Theming @@ -14,16 +14,23 @@ Every react-admin component provides a `className` property, which is always app Here is an example customizing an `EditButton` component inside a `Datagrid`, using its `className` property and the `makeStyles` hook from Material-UI: {% raw %} + ```jsx import * as React from 'react'; -import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; +import { + NumberField, + List, + Datagrid, + TextField, + EditButton, +} from 'react-admin'; import { makeStyles } from '@material-ui/core/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' } + '& svg': { color: 'orange' }, }, }); @@ -32,7 +39,7 @@ const MyEditButton = props => { return ; }; -export const ProductList = (props) => ( +export const ProductList = props => ( @@ -42,6 +49,7 @@ export const ProductList = (props) => ( ); ``` + {% endraw %} For some components, you may want to override not only the root component style, but also the style of components inside the root. In this case, the `className` property isn't enough. You can take advantage of the `classes` property to customize the classes that the component uses internally. @@ -49,6 +57,7 @@ For some components, you may want to override not only the root component style, Here is an example using the `classes` property of the `` component: {% raw %} + ```jsx import * as React from 'react'; import { @@ -66,7 +75,7 @@ import { makeStyles } from '@material-ui/core/styles'; export const VisitorIcon = Icon; -// The `Datagrid` component uses makeStyles, and supports overriding styles through the `classes` property +// The `Datagrid` component uses makeStyles, and supports overriding styles through the `classes` property const useStyles = makeStyles({ table: { backgroundColor: 'Lavender', @@ -83,16 +92,17 @@ export const PostList = props => { - + - ) + ); }; ``` + {% endraw %} This example results in: @@ -108,9 +118,16 @@ If you need more control over the HTML code, you can also create your own [Field Sometimes you want the format to depend on the value. The following example shows how to create a new custom `NumberField` component which highlight its text in red when its value is 100 or higher. {% raw %} + ```jsx import * as React from 'react'; -import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; +import { + NumberField, + List, + Datagrid, + TextField, + EditButton, +} from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -146,14 +163,22 @@ export const PostList = props => ( ); ``` + {% endraw %} Furthermore, you may extract this highlighting strategy into a Higher Order Component if you'd like to reuse it for other components as well: {% raw %} + ```jsx import * as React from 'react'; -import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; +import { + NumberField, + List, + Datagrid, + TextField, + EditButton, +} from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -172,15 +197,14 @@ const colored = WrappedComponent => props => { })} {...props} /> - ) + ); }; - const ColoredNumberField = colored(NumberField); // 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 => ( @@ -191,6 +215,7 @@ export const PostList = (props) => ( ); ``` + {% endraw %} 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/) @@ -219,9 +244,16 @@ Here is an example for a responsive list of posts, displaying a `SimpleList` on // in src/posts.js import * as React from 'react'; import { useMediaQuery } from '@material-ui/core'; -import { List, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; +import { + List, + SimpleList, + Datagrid, + TextField, + ReferenceField, + EditButton, +} from 'react-admin'; -export const PostList = (props) => { +export const PostList = props => { const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); return ( @@ -229,12 +261,18 @@ export const PostList = (props) => { record.title} secondaryText={record => `${record.views} views`} - tertiaryText={record => new Date(record.published_at).toLocaleDateString()} + tertiaryText={record => + new Date(record.published_at).toLocaleDateString() + } /> ) : ( - + @@ -257,13 +295,16 @@ Material UI also supports [complete theming](https://material-ui.com/customizati import { createMuiTheme } from '@material-ui/core/styles'; const theme = createMuiTheme({ - palette: { - type: 'dark', // Switching the dark mode on is a single property value change. - }, + palette: { + type: 'dark', // Switching the dark mode on is a single property value change. + }, }); const App = () => ( - + // ... ); @@ -294,11 +335,19 @@ const myTheme = merge({}, defaultTheme, { }, typography: { // Use the system font instead of the default Roboto font. - fontFamily: ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Arial', 'sans-serif'].join(','), + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Arial', + 'sans-serif', + ].join(','), }, overrides: { - MuiButton: { // override the styles of all instances of this component - root: { // Name of the rule + MuiButton: { + // override the styles of all instances of this component + root: { + // Name of the rule color: 'white', // Some CSS }, }, @@ -308,17 +357,17 @@ const myTheme = merge({}, defaultTheme, { A `theme` object can contain the following keys: -* `breakpoints` -* `direction` -* `mixins` -* `overrides` -* `palette` -* `props` -* `shadows` -* `spacing` -* `transitions` -* `typography` -* `zIndex` +- `breakpoints` +- `direction` +- `mixins` +- `overrides` +- `palette` +- `props` +- `shadows` +- `spacing` +- `transitions` +- `typography` +- `zIndex` **Tip**: Check [Material UI default theme documentation](https://material-ui.com/customization/default-theme/) to see the default values and meaning for these keys. @@ -326,7 +375,10 @@ Once your theme is defined, pass it to the `` component, in the `theme` p ```jsx const App = () => ( - + // ... ); @@ -341,7 +393,10 @@ Instead of the default layout, you can use your own component as the admin layou import MyLayout from './MyLayout'; const App = () => ( - + // ... ); @@ -357,13 +412,15 @@ import MySidebar from './MySidebar'; import MyMenu from './MyMenu'; import MyNotification from './MyNotification'; -const MyLayout = props => ; +const MyLayout = props => ( + +); export default MyLayout; ``` @@ -412,7 +469,8 @@ const MyLayout = props => ; You can also customize the default icon by setting the `icon` prop to the `` component. {% raw %} -``` jsx + +```jsx import { AppBar, UserMenu } from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import Avatar from '@material-ui/core/Avatar'; @@ -431,13 +489,14 @@ const MyCustomIcon = () => { className={classes.avatar} src="https://marmelab.com/images/avatars/adrien.jpg" /> - ) + ); }; -const MyUserMenu = props => (} />); +const MyUserMenu = props => } />; const MyAppBar = props => } />; ``` + {% endraw %} ### Sidebar Customization @@ -445,7 +504,7 @@ const MyAppBar = props => } />; You can specify the `Sidebar` width by setting the `width` and `closedWidth` property on your custom material-ui theme: ```jsx -import { defaultTheme } from "react-admin"; +import { defaultTheme } from 'react-admin'; import { createMuiTheme } from '@material-ui/core/styles'; const theme = createMuiTheme({ @@ -457,7 +516,10 @@ const theme = createMuiTheme({ }); const App = () => ( - + // ... ); @@ -477,12 +539,10 @@ const useSidebarStyles = makeStyles({ const MySidebar = props => { const classes = useSidebarStyles(); - return ( - - ); + return ; }; -const MyLayout = props => +const MyLayout = props => ; ``` ### Layout From Scratch @@ -534,12 +594,7 @@ const useStyles = makeStyles(theme => ({ }, })); -const MyLayout = ({ - children, - dashboard, - logout, - title, -}) => { +const MyLayout = ({ children, dashboard, logout, title }) => { const classes = useStyles(); const dispatch = useDispatch(); const open = useSelector(state => state.admin.ui.sidebarOpen); @@ -556,9 +611,7 @@ const MyLayout = ({ -
- {children} -
+
{children}
@@ -568,10 +621,7 @@ const MyLayout = ({ MyLayout.propTypes = { children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - dashboard: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.string, - ]), + dashboard: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), logout: ComponentPropType, title: PropTypes.string.isRequired, }; @@ -679,7 +729,7 @@ import * as React from 'react'; import { Layout } from 'react-admin'; import MyAppBar from './MyAppBar'; -const MyLayout = (props) => ; +const MyLayout = props => ; export default MyLayout; ``` @@ -691,7 +741,10 @@ Then, use this layout in the `` with the `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - + // ... ); @@ -711,9 +764,7 @@ import * as React from 'react'; import { Fragment } from 'react'; import { AppBar } from 'react-admin'; -const MyAppBar = props => ( - -); +const MyAppBar = props => ; export default MyAppBar; ``` @@ -738,7 +789,13 @@ const MyAppBar = props => ( export default MyAppBar; ``` -Take note that this uses *material-ui's ``* instead of *react-admin's ``*. To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained in the previous section. +Take note that this uses _material-ui's ``_ instead of _react-admin's ``_. To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained in the previous section. + +To make it easier to customize, we export some of the components and hooks used by the ``: + +- ``: A `CircularProgress` bound to the dataProvider activity. +- ``: An `IconButton` used to toggle the ``. +- `useToggleSidebar`: A hook that returns the sidebar open state and a function to toggle it. Used internally by ``. ## Adding Dark Mode Support @@ -788,10 +845,22 @@ import LabelIcon from '@material-ui/icons/Label'; export const Menu = () => (
- }/> - }/> - }/> - }/> + } /> + } + /> + } + /> + } + />
); ``` @@ -803,23 +872,26 @@ To use this custom menu component, pass it to a custom Layout, as explained abov import { Layout } from 'react-admin'; import { Menu } from './Menu'; -export const Layout = (props) => ; +export const Layout = props => ; ``` Then, use this layout in the `` `layout` prop: ```jsx // in src/App.js -import { Layout } from './Layout'; +import { Layout } from './Layout'; const App = () => ( - + // ... ); ``` -**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations from the Redux store: +**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations from the Redux store: ```jsx // in src/Menu.js @@ -872,11 +944,14 @@ The `primaryText` prop accepts a string or a React node. You can use it e.g. to ```jsx import Badge from '@material-ui/core/Badge'; - - Notifications - -} /> + + Notifications + + } +/>; ``` The `letfIcon` prop allows to set the menu left icon. @@ -910,6 +985,7 @@ As the filter values are taken from the URL, you can link to a pre-filtered list For instance, to include a menu to a list of published posts: {% raw %} + ```jsx } /> ``` + {% endraw %} ### Menu To A List Without Filters @@ -952,11 +1029,7 @@ const MyLoginPage = () => ( /> ); -const App = () => ( - - // ... - -); +const App = () => // ...; ``` ## Using a Custom Logout Button @@ -969,13 +1042,9 @@ It is possible to use a completely [custom logout button](./Admin.md#logoutbutto import { Admin, Logout } from 'react-admin'; import ExitToAppIcon from '@material-ui/icons/ExitToApp'; -const MyLogoutButton = props => } />; +const MyLogoutButton = props => } />; -const App = () => ( - - // ... - -); +const App = () => // ...; ``` ## Notifications @@ -986,7 +1055,9 @@ You can override the notification component, for instance to change the notifica // in src/MyNotification.js import { Notification } from 'react-admin'; -const MyNotification = props => ; +const MyNotification = props => ( + +); export default MyNotification; ``` @@ -1000,7 +1071,7 @@ To use this custom notification component, pass it to a custom Layout, as explai import { Layout } from 'react-admin'; import MyNotification from './MyNotification'; -const MyLayout = (props) => ; +const MyLayout = props => ; export default MyLayout; ``` @@ -1012,7 +1083,10 @@ Then, use this layout in the `` `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - + // ... ); @@ -1030,17 +1104,17 @@ import ErrorIcon from '@material-ui/icons/Report'; import History from '@material-ui/icons/History'; import { Title, useTranslate } from 'react-admin'; -const MyError = ({ - error, - errorInfo, - ...rest -}) => { +const MyError = ({ error, errorInfo, ...rest }) => { const translate = useTranslate(); return (
- <h1><ErrorIcon /> Something Went Wrong </h1> - <div>A client error occurred and your request couldn't be completed.</div> + <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> @@ -1070,7 +1144,7 @@ To use this custom error component, pass it to a custom Layout, as explained abo import { Layout } from 'react-admin'; import MyError from './MyError'; -const MyLayout = (props) => <Layout {...props} error={MyError} />; +const MyLayout = props => <Layout {...props} error={MyError} />; export default MyLayout; ``` @@ -1082,7 +1156,10 @@ Then, use this layout in the `<Admin>` `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}> + <Admin + layout={MyLayout} + dataProvider={simpleRestProvider('http://path.to.my.api')} + > // ... </Admin> ); @@ -1094,15 +1171,18 @@ Display a circular progress component with optional messages. Display the same l Supported props: -| Prop | Required | Type | Default | Descriptions | -| ------------------ | -------- | --------- | -------------------- | ------------------------------------------ | -| `loadingPrimary` | Optional | `string` | `ra.page.loading` | Label to use for primary loading message | -| `loadingSecondary` | Optional | `string` | `ra.message.loading` | Label to use for secondary loading message | +| Prop | Required | Type | Default | Descriptions | +| ------------------ | -------- | -------- | -------------------- | ------------------------------------------ | +| `loadingPrimary` | Optional | `string` | `ra.page.loading` | Label to use for primary loading message | +| `loadingSecondary` | Optional | `string` | `ra.message.loading` | Label to use for secondary loading message | Usage: ```jsx -<Loading loadingPrimary="app.page.loading" loadingSecondary="app.message.loading" /> +<Loading + loadingPrimary="app.page.loading" + loadingSecondary="app.message.loading" +/> ``` ## LinearProgress @@ -1112,7 +1192,5 @@ Display a linear progress component. Display the same loading component as `reac Usage: ```jsx -({ data, ...props }) => !data ? - <LinearProgress /> : - <MyInput data={data} />; +({ data, ...props }) => (!data ? <LinearProgress /> : <MyInput data={data} />); ``` diff --git a/packages/ra-ui-materialui/src/layout/AppBar.tsx b/packages/ra-ui-materialui/src/layout/AppBar.tsx index eb44541a1aa..c2e58ad8f30 100644 --- a/packages/ra-ui-materialui/src/layout/AppBar.tsx +++ b/packages/ra-ui-materialui/src/layout/AppBar.tsx @@ -1,60 +1,23 @@ import * as React from 'react'; import { Children, cloneElement, memo } from 'react'; import PropTypes from 'prop-types'; -import { useDispatch } from 'react-redux'; -import classNames from 'classnames'; import { AppBar as MuiAppBar, AppBarProps as MuiAppBarProps, - IconButton, Toolbar, - Tooltip, Typography, useMediaQuery, Theme, } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; -import MenuIcon from '@material-ui/icons/Menu'; -import { toggleSidebar, useTranslate, ComponentPropType } from 'ra-core'; +import { ComponentPropType } from 'ra-core'; +import { SidebarToggleButton } from './SidebarToggleButton'; import LoadingIndicator from './LoadingIndicator'; import DefaultUserMenu from './UserMenu'; import HideOnScroll from './HideOnScroll'; import { ClassesOverride } from '../types'; -const useStyles = makeStyles( - theme => ({ - toolbar: { - paddingRight: 24, - }, - menuButton: { - marginLeft: '0.2em', - marginRight: '0.2em', - }, - menuButtonIconClosed: { - transition: theme.transitions.create(['transform'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - transform: 'rotate(0deg)', - }, - menuButtonIconOpen: { - transition: theme.transitions.create(['transform'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - transform: 'rotate(180deg)', - }, - title: { - flex: 1, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - overflow: 'hidden', - }, - }), - { name: 'RaAppBar' } -); - /** * The AppBar component renders a custom MuiAppBar. * @@ -106,11 +69,13 @@ const AppBar = (props: AppBarProps): JSX.Element => { ...rest } = props; const classes = useStyles(props); - const dispatch = useDispatch(); + const sidebarToggleButtonClasses = { + menuButtonIconClosed: classes.menuButtonIconClosed, + menuButtonIconOpen: classes.menuButtonIconOpen, + }; const isXSmall = useMediaQuery<Theme>(theme => theme.breakpoints.down('xs') ); - const translate = useTranslate(); return ( <Container> @@ -120,31 +85,10 @@ const AppBar = (props: AppBarProps): JSX.Element => { variant={isXSmall ? 'regular' : 'dense'} className={classes.toolbar} > - <Tooltip - title={translate( - open - ? 'ra.action.close_menu' - : 'ra.action.open_menu', - { - _: 'Open/Close menu', - } - )} - enterDelay={500} - > - <IconButton - color="inherit" - onClick={() => dispatch(toggleSidebar())} - className={classNames(classes.menuButton)} - > - <MenuIcon - classes={{ - root: open - ? classes.menuButtonIconOpen - : classes.menuButtonIconClosed, - }} - /> - </IconButton> - </Tooltip> + <SidebarToggleButton + className={classes.menuButton} + classes={sidebarToggleButtonClasses} + /> {Children.count(children) === 0 ? ( <Typography variant="h6" @@ -183,6 +127,7 @@ AppBar.propTypes = { ]), container: ComponentPropType, logout: PropTypes.element, + // @deprecated open: PropTypes.bool, userMenu: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), }; @@ -192,10 +137,32 @@ AppBar.defaultProps = { container: HideOnScroll, }; +const useStyles = makeStyles( + theme => ({ + toolbar: { + paddingRight: 24, + }, + menuButton: { + marginLeft: '0.2em', + marginRight: '0.2em', + }, + menuButtonIconClosed: {}, + menuButtonIconOpen: {}, + title: { + flex: 1, + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + }, + }), + { name: 'RaAppBar' } +); + export interface AppBarProps extends Omit<MuiAppBarProps, 'title' | 'classes'> { classes?: ClassesOverride<typeof useStyles>; container?: React.ElementType<any>; logout?: React.ReactNode; + // @deprecated open?: boolean; title?: string | JSX.Element; userMenu?: JSX.Element | boolean; diff --git a/packages/ra-ui-materialui/src/layout/SidebarToggleButton.tsx b/packages/ra-ui-materialui/src/layout/SidebarToggleButton.tsx new file mode 100644 index 00000000000..be13bf162e1 --- /dev/null +++ b/packages/ra-ui-materialui/src/layout/SidebarToggleButton.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { IconButton, Tooltip } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import MenuIcon from '@material-ui/icons/Menu'; +import { useTranslate } from 'ra-core'; +import { ClassesOverride } from '../types'; +import { useToggleSidebar } from './useToggleSidebar'; + +/** + * A button that toggles the sidebar. Used by default in the <AppBar>. + * @param props The component props + * @param {String} props.className An optional class name to apply to the button + * @param {ClassesOverride<typeof useStyles>} props.classes An object containing styles. + */ +export const SidebarToggleButton = (props: SidebarToggleButtonProps) => { + const translate = useTranslate(); + const classes = useStyles(props); + const { className } = props; + const [open, toggleSidebar] = useToggleSidebar(); + + return ( + <Tooltip + title={translate( + open ? 'ra.action.close_menu' : 'ra.action.open_menu', + { + _: 'Open/Close menu', + } + )} + enterDelay={500} + > + <IconButton + color="inherit" + onClick={() => toggleSidebar()} + className={className} + > + <MenuIcon + classes={{ + root: open + ? classes.menuButtonIconOpen + : classes.menuButtonIconClosed, + }} + /> + </IconButton> + </Tooltip> + ); +}; + +const useStyles = makeStyles( + theme => ({ + menuButtonIconClosed: { + transition: theme.transitions.create(['transform'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + transform: 'rotate(0deg)', + }, + menuButtonIconOpen: { + transition: theme.transitions.create(['transform'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + transform: 'rotate(180deg)', + }, + }), + { name: 'RaSidebarToggleButton' } +); + +export type SidebarToggleButtonProps = { + className?: string; + classes?: ClassesOverride<typeof useStyles>; +}; diff --git a/packages/ra-ui-materialui/src/layout/useToggleSidebar.ts b/packages/ra-ui-materialui/src/layout/useToggleSidebar.ts new file mode 100644 index 00000000000..507c9703f32 --- /dev/null +++ b/packages/ra-ui-materialui/src/layout/useToggleSidebar.ts @@ -0,0 +1,29 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { ReduxState, toggleSidebar } from 'ra-core'; + +/** + * A hook that returns the sidebar open state and a function to toggle it. + * @returns A tuple containing a boolean indicating whether the sidebar is open or not and a function to toggle the sidebar. + * @example + * const MyButton = () => { + * const [sidebarOpen, toggleSidebar] = useToggleSidebar(); + * return ( + * <Button + * color="inherit" + * onClick={() => toggleSidebar()} + * > + * {open ? 'Open' : 'Close'} + * </Button> + * ); + */ +export const useToggleSidebar = (): UseToggleSidebarResult => { + const open = useSelector<ReduxState, boolean>( + state => state.admin.ui.sidebarOpen + ); + const dispatch = useDispatch(); + + const toggle = () => dispatch(toggleSidebar()); + return [open, toggle]; +}; + +export type UseToggleSidebarResult = [boolean, () => void]; From 58cb27fb80067ca5d0ae2261b20ef6503c4578da Mon Sep 17 00:00:00 2001 From: Travis CI <travis@travis-ci.org> Date: Thu, 23 Sep 2021 10:14:31 +0200 Subject: [PATCH 2/3] Revert prettier changes --- docs/Theming.md | 308 +++++++++++++++++++----------------------------- 1 file changed, 118 insertions(+), 190 deletions(-) diff --git a/docs/Theming.md b/docs/Theming.md index c5724fcb125..9c97f553103 100644 --- a/docs/Theming.md +++ b/docs/Theming.md @@ -1,6 +1,6 @@ --- layout: default -title: 'Theming' +title: "Theming" --- # Theming @@ -14,23 +14,16 @@ Every react-admin component provides a `className` property, which is always app Here is an example customizing an `EditButton` component inside a `Datagrid`, using its `className` property and the `makeStyles` hook from Material-UI: {% raw %} - ```jsx import * as React from 'react'; -import { - NumberField, - List, - Datagrid, - TextField, - EditButton, -} from 'react-admin'; +import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; import { makeStyles } from '@material-ui/core/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' }, + '& svg': { color: 'orange' } }, }); @@ -39,7 +32,7 @@ const MyEditButton = props => { return <EditButton className={classes.button} {...props} />; }; -export const ProductList = props => ( +export const ProductList = (props) => ( <List {...props}> <Datagrid> <TextField source="sku" /> @@ -49,7 +42,6 @@ export const ProductList = props => ( </List> ); ``` - {% endraw %} For some components, you may want to override not only the root component style, but also the style of components inside the root. In this case, the `className` property isn't enough. You can take advantage of the `classes` property to customize the classes that the component uses internally. @@ -57,7 +49,6 @@ For some components, you may want to override not only the root component style, Here is an example using the `classes` property of the `<Datagrid>` component: {% raw %} - ```jsx import * as React from 'react'; import { @@ -75,7 +66,7 @@ import { makeStyles } from '@material-ui/core/styles'; export const VisitorIcon = Icon; -// The `Datagrid` component uses makeStyles, and supports overriding styles through the `classes` property +// The `Datagrid` component uses makeStyles, and supports overriding styles through the `classes` property const useStyles = makeStyles({ table: { backgroundColor: 'Lavender', @@ -92,17 +83,16 @@ export const PostList = props => { <Datagrid classes={classes} {...props}> <TextField source="id" /> <TextField source="title" /> - <DateField source="published_at" sortByOrder="DESC" /> + <DateField source="published_at" sortByOrder="DESC"/> <BooleanField source="commentable" sortable={false} /> <NumberField source="views" sortByOrder="DESC" /> <EditButton /> <ShowButton /> </Datagrid> </List> - ); + ) }; ``` - {% endraw %} This example results in: @@ -118,16 +108,9 @@ If you need more control over the HTML code, you can also create your own [Field Sometimes you want the format to depend on the value. The following example shows how to create a new custom `NumberField` component which highlight its text in red when its value is 100 or higher. {% raw %} - ```jsx import * as React from 'react'; -import { - NumberField, - List, - Datagrid, - TextField, - EditButton, -} from 'react-admin'; +import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -163,22 +146,14 @@ export const PostList = props => ( </List> ); ``` - {% endraw %} Furthermore, you may extract this highlighting strategy into a Higher Order Component if you'd like to reuse it for other components as well: {% raw %} - ```jsx import * as React from 'react'; -import { - NumberField, - List, - Datagrid, - TextField, - EditButton, -} from 'react-admin'; +import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -197,14 +172,15 @@ const colored = WrappedComponent => props => { })} {...props} /> - ); + ) }; + const ColoredNumberField = colored(NumberField); // 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) => ( <List {...props}> <Datagrid> <TextField source="id" /> @@ -215,7 +191,6 @@ export const PostList = props => ( </List> ); ``` - {% endraw %} 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/) @@ -244,16 +219,9 @@ Here is an example for a responsive list of posts, displaying a `SimpleList` on // in src/posts.js import * as React from 'react'; import { useMediaQuery } from '@material-ui/core'; -import { - List, - SimpleList, - Datagrid, - TextField, - ReferenceField, - EditButton, -} from 'react-admin'; +import { List, SimpleList, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin'; -export const PostList = props => { +export const PostList = (props) => { const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); return ( <List {...props}> @@ -261,18 +229,12 @@ export const PostList = props => { <SimpleList primaryText={record => record.title} secondaryText={record => `${record.views} views`} - tertiaryText={record => - new Date(record.published_at).toLocaleDateString() - } + tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> ) : ( <Datagrid> <TextField source="id" /> - <ReferenceField - label="User" - source="userId" - reference="users" - > + <ReferenceField label="User" source="userId" reference="users"> <TextField source="name" /> </ReferenceField> <TextField source="title" /> @@ -295,16 +257,13 @@ Material UI also supports [complete theming](https://material-ui.com/customizati import { createMuiTheme } from '@material-ui/core/styles'; const theme = createMuiTheme({ - palette: { - type: 'dark', // Switching the dark mode on is a single property value change. - }, + palette: { + type: 'dark', // Switching the dark mode on is a single property value change. + }, }); const App = () => ( - <Admin - theme={theme} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin theme={theme} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -335,19 +294,11 @@ const myTheme = merge({}, defaultTheme, { }, typography: { // Use the system font instead of the default Roboto font. - fontFamily: [ - '-apple-system', - 'BlinkMacSystemFont', - '"Segoe UI"', - 'Arial', - 'sans-serif', - ].join(','), + fontFamily: ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Arial', 'sans-serif'].join(','), }, overrides: { - MuiButton: { - // override the styles of all instances of this component - root: { - // Name of the rule + MuiButton: { // override the styles of all instances of this component + root: { // Name of the rule color: 'white', // Some CSS }, }, @@ -357,17 +308,17 @@ const myTheme = merge({}, defaultTheme, { A `theme` object can contain the following keys: -- `breakpoints` -- `direction` -- `mixins` -- `overrides` -- `palette` -- `props` -- `shadows` -- `spacing` -- `transitions` -- `typography` -- `zIndex` +* `breakpoints` +* `direction` +* `mixins` +* `overrides` +* `palette` +* `props` +* `shadows` +* `spacing` +* `transitions` +* `typography` +* `zIndex` **Tip**: Check [Material UI default theme documentation](https://material-ui.com/customization/default-theme/) to see the default values and meaning for these keys. @@ -375,10 +326,7 @@ Once your theme is defined, pass it to the `<Admin>` component, in the `theme` p ```jsx const App = () => ( - <Admin - theme={myTheme} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin theme={myTheme} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -393,10 +341,7 @@ Instead of the default layout, you can use your own component as the admin layou import MyLayout from './MyLayout'; const App = () => ( - <Admin - layout={MyLayout} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -412,15 +357,13 @@ import MySidebar from './MySidebar'; import MyMenu from './MyMenu'; import MyNotification from './MyNotification'; -const MyLayout = props => ( - <Layout - {...props} - appBar={MyAppBar} - sidebar={MySidebar} - menu={MyMenu} - notification={MyNotification} - /> -); +const MyLayout = props => <Layout + {...props} + appBar={MyAppBar} + sidebar={MySidebar} + menu={MyMenu} + notification={MyNotification} +/>; export default MyLayout; ``` @@ -469,8 +412,7 @@ const MyLayout = props => <Layout {...props} appBar={MyAppBar} />; You can also customize the default icon by setting the `icon` prop to the `<UserMenu />` component. {% raw %} - -```jsx +``` jsx import { AppBar, UserMenu } from 'react-admin'; import { makeStyles } from '@material-ui/core/styles'; import Avatar from '@material-ui/core/Avatar'; @@ -489,14 +431,13 @@ const MyCustomIcon = () => { className={classes.avatar} src="https://marmelab.com/images/avatars/adrien.jpg" /> - ); + ) }; -const MyUserMenu = props => <UserMenu {...props} icon={<MyCustomIcon />} />; +const MyUserMenu = props => (<UserMenu {...props} icon={<MyCustomIcon />} />); const MyAppBar = props => <AppBar {...props} userMenu={<MyUserMenu />} />; ``` - {% endraw %} ### Sidebar Customization @@ -504,7 +445,7 @@ const MyAppBar = props => <AppBar {...props} userMenu={<MyUserMenu />} />; You can specify the `Sidebar` width by setting the `width` and `closedWidth` property on your custom material-ui theme: ```jsx -import { defaultTheme } from 'react-admin'; +import { defaultTheme } from "react-admin"; import { createMuiTheme } from '@material-ui/core/styles'; const theme = createMuiTheme({ @@ -516,10 +457,7 @@ const theme = createMuiTheme({ }); const App = () => ( - <Admin - theme={theme} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin theme={theme} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -539,10 +477,12 @@ const useSidebarStyles = makeStyles({ const MySidebar = props => { const classes = useSidebarStyles(); - return <Sidebar classes={classes} {...props} />; + return ( + <Sidebar classes={classes} {...props} /> + ); }; -const MyLayout = props => <Layout {...props} sidebar={MySidebar} />; +const MyLayout = props => <Layout {...props} sidebar={MySidebar} /> ``` ### Layout From Scratch @@ -594,7 +534,12 @@ const useStyles = makeStyles(theme => ({ }, })); -const MyLayout = ({ children, dashboard, logout, title }) => { +const MyLayout = ({ + children, + dashboard, + logout, + title, +}) => { const classes = useStyles(); const dispatch = useDispatch(); const open = useSelector(state => state.admin.ui.sidebarOpen); @@ -611,7 +556,9 @@ const MyLayout = ({ children, dashboard, logout, title }) => { <Sidebar> <Menu logout={logout} hasDashboard={!!dashboard} /> </Sidebar> - <div className={classes.content}>{children}</div> + <div className={classes.content}> + {children} + </div> </main> <Notification /> </div> @@ -621,7 +568,10 @@ const MyLayout = ({ children, dashboard, logout, title }) => { MyLayout.propTypes = { children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - dashboard: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + dashboard: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string, + ]), logout: ComponentPropType, title: PropTypes.string.isRequired, }; @@ -729,7 +679,7 @@ import * as React from 'react'; import { Layout } from 'react-admin'; import MyAppBar from './MyAppBar'; -const MyLayout = props => <Layout {...props} appBar={MyAppBar} />; +const MyLayout = (props) => <Layout {...props} appBar={MyAppBar} />; export default MyLayout; ``` @@ -741,10 +691,7 @@ Then, use this layout in the `<Admin>` with the `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - <Admin - layout={MyLayout} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -764,7 +711,9 @@ import * as React from 'react'; import { Fragment } from 'react'; import { AppBar } from 'react-admin'; -const MyAppBar = props => <AppBar {...props} container={Fragment} />; +const MyAppBar = props => ( + <AppBar {...props} container={Fragment} /> +); export default MyAppBar; ``` @@ -789,13 +738,13 @@ const MyAppBar = props => ( export default MyAppBar; ``` -Take note that this uses _material-ui's `<AppBar>`_ instead of _react-admin's `<AppBar>`_. To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained in the previous section. +Take note that this uses *material-ui's `<AppBar>`* instead of *react-admin's `<AppBar>`*. To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained in the previous section. To make it easier to customize, we export some of the components and hooks used by the `<AppBar>`: -- `<LoadingIndicator>`: A `CircularProgress` bound to the dataProvider activity. -- `<SidebarToggleButton>`: An `IconButton` used to toggle the `<Sidebar>`. -- `useToggleSidebar`: A hook that returns the sidebar open state and a function to toggle it. Used internally by `<SidebarToggleButton>`. +- `<LoadingIndicator>`: A `CircularProgress` bound to the dataProvider activity. +- `<SidebarToggleButton>`: An `IconButton` used to toggle the `<Sidebar>`. +- `useToggleSidebar`: A hook that returns the sidebar open state and a function to toggle it. Used internally by `<SidebarToggleButton>`. ## Adding Dark Mode Support @@ -845,22 +794,10 @@ 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 />} - /> + <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> ); ``` @@ -872,26 +809,23 @@ To use this custom menu component, pass it to a custom Layout, as explained abov import { Layout } from 'react-admin'; import { Menu } from './Menu'; -export const Layout = props => <Layout {...props} menu={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'; +import { Layout } from './Layout'; const App = () => ( - <Admin - layout={Layout} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin layout={Layout} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); ``` -**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations from the Redux store: +**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations from the Redux store: ```jsx // in src/Menu.js @@ -944,14 +878,11 @@ The `primaryText` prop accepts a string or a React node. You can use it e.g. to ```jsx import Badge from '@material-ui/core/Badge'; -<MenuItemLink - to="/custom-route" - primaryText={ - <Badge badgeContent={4} color="primary"> - Notifications - </Badge> - } -/>; +<MenuItemLink to="/custom-route" primaryText={ + <Badge badgeContent={4} color="primary"> + Notifications + </Badge> +} /> ``` The `letfIcon` prop allows to set the menu left icon. @@ -985,7 +916,6 @@ As the filter values are taken from the URL, you can link to a pre-filtered list For instance, to include a menu to a list of published posts: {% raw %} - ```jsx <MenuItemLink to={{ @@ -996,7 +926,6 @@ For instance, to include a menu to a list of published posts: leftIcon={<BookIcon />} /> ``` - {% endraw %} ### Menu To A List Without Filters @@ -1029,7 +958,11 @@ const MyLoginPage = () => ( /> ); -const App = () => <Admin loginPage={MyLoginPage}>// ...</Admin>; +const App = () => ( + <Admin loginPage={MyLoginPage}> + // ... + </Admin> +); ``` ## Using a Custom Logout Button @@ -1042,9 +975,13 @@ It is possible to use a completely [custom logout button](./Admin.md#logoutbutto import { Admin, Logout } from 'react-admin'; import ExitToAppIcon from '@material-ui/icons/ExitToApp'; -const MyLogoutButton = props => <Logout {...props} icon={<ExitToAppIcon />} />; +const MyLogoutButton = props => <Logout {...props} icon={<ExitToAppIcon/>} />; -const App = () => <Admin logoutButton={MyLogoutButton}>// ...</Admin>; +const App = () => ( + <Admin logoutButton={MyLogoutButton}> + // ... + </Admin> +); ``` ## Notifications @@ -1055,9 +992,7 @@ You can override the notification component, for instance to change the notifica // in src/MyNotification.js import { Notification } from 'react-admin'; -const MyNotification = props => ( - <Notification {...props} autoHideDuration={5000} /> -); +const MyNotification = props => <Notification {...props} autoHideDuration={5000} />; export default MyNotification; ``` @@ -1071,7 +1006,7 @@ To use this custom notification component, pass it to a custom Layout, as explai import { Layout } from 'react-admin'; import MyNotification from './MyNotification'; -const MyLayout = props => <Layout {...props} notification={MyNotification} />; +const MyLayout = (props) => <Layout {...props} notification={MyNotification} />; export default MyLayout; ``` @@ -1083,10 +1018,7 @@ Then, use this layout in the `<Admin>` `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - <Admin - layout={MyLayout} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -1104,17 +1036,17 @@ import ErrorIcon from '@material-ui/icons/Report'; import History from '@material-ui/icons/History'; import { Title, useTranslate } from 'react-admin'; -const MyError = ({ error, errorInfo, ...rest }) => { +const MyError = ({ + error, + errorInfo, + ...rest +}) => { const translate = useTranslate(); return ( <div> <Title title="Error" /> - <h1> - <ErrorIcon /> Something Went Wrong{' '} - </h1> - <div> - A client error occurred and your request couldn't be completed. - </div> + <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> @@ -1144,7 +1076,7 @@ To use this custom error component, pass it to a custom Layout, as explained abo import { Layout } from 'react-admin'; import MyError from './MyError'; -const MyLayout = props => <Layout {...props} error={MyError} />; +const MyLayout = (props) => <Layout {...props} error={MyError} />; export default MyLayout; ``` @@ -1156,10 +1088,7 @@ Then, use this layout in the `<Admin>` `layout` prop: import MyLayout from './MyLayout'; const App = () => ( - <Admin - layout={MyLayout} - dataProvider={simpleRestProvider('http://path.to.my.api')} - > + <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}> // ... </Admin> ); @@ -1171,18 +1100,15 @@ Display a circular progress component with optional messages. Display the same l Supported props: -| Prop | Required | Type | Default | Descriptions | -| ------------------ | -------- | -------- | -------------------- | ------------------------------------------ | -| `loadingPrimary` | Optional | `string` | `ra.page.loading` | Label to use for primary loading message | -| `loadingSecondary` | Optional | `string` | `ra.message.loading` | Label to use for secondary loading message | +| Prop | Required | Type | Default | Descriptions | +| ------------------ | -------- | --------- | -------------------- | ------------------------------------------ | +| `loadingPrimary` | Optional | `string` | `ra.page.loading` | Label to use for primary loading message | +| `loadingSecondary` | Optional | `string` | `ra.message.loading` | Label to use for secondary loading message | Usage: ```jsx -<Loading - loadingPrimary="app.page.loading" - loadingSecondary="app.message.loading" -/> +<Loading loadingPrimary="app.page.loading" loadingSecondary="app.message.loading" /> ``` ## LinearProgress @@ -1192,5 +1118,7 @@ Display a linear progress component. Display the same loading component as `reac Usage: ```jsx -({ data, ...props }) => (!data ? <LinearProgress /> : <MyInput data={data} />); +({ data, ...props }) => !data ? + <LinearProgress /> : + <MyInput data={data} />; ``` From b6883cc0124a2cf371e19a481444009cb4ea9eab Mon Sep 17 00:00:00 2001 From: Travis CI <travis@travis-ci.org> Date: Thu, 23 Sep 2021 10:14:45 +0200 Subject: [PATCH 3/3] Ensure prettier never update markdown files --- .prettierignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..dd449725e18 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.md