From bcf850e3c16f0d2984ee6845f6e04ac27443c40e Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah Date: Fri, 29 May 2020 14:23:42 +0200 Subject: [PATCH 1/7] convert ra-ui-materialui layout to typescript --- packages/ra-ui-materialui/src/defaultTheme.ts | 29 ++++- ...rdContentInner.js => CardContentInner.tsx} | 10 +- ...ceTestWrapper.js => DeviceTestWrapper.tsx} | 9 +- .../src/layout/{Error.js => Error.tsx} | 26 +++-- .../src/layout/{Layout.js => Layout.tsx} | 110 ++++++++++++------ .../{LinearProgress.js => LinearProgress.tsx} | 8 +- .../src/layout/{Loading.js => Loading.tsx} | 11 +- ...adingIndicator.js => LoadingIndicator.tsx} | 14 ++- packages/ra-ui-materialui/src/layout/Menu.tsx | 4 +- .../src/layout/{NotFound.js => NotFound.tsx} | 20 ++-- .../src/layout/Notification.tsx | 8 +- .../src/layout/{Sidebar.js => Sidebar.tsx} | 39 ++++--- .../src/layout/{Title.js => Title.tsx} | 20 +++- .../src/layout/TitleForRecord.js | 18 --- .../src/layout/TitleForRecord.tsx | 18 +++ .../layout/{TopToolbar.js => TopToolbar.tsx} | 6 +- .../src/layout/{UserMenu.js => UserMenu.tsx} | 19 ++- .../src/layout/{index.js => index.ts} | 0 18 files changed, 238 insertions(+), 131 deletions(-) rename packages/ra-ui-materialui/src/layout/{CardContentInner.js => CardContentInner.tsx} (83%) rename packages/ra-ui-materialui/src/layout/{DeviceTestWrapper.js => DeviceTestWrapper.tsx} (82%) rename packages/ra-ui-materialui/src/layout/{Error.js => Error.tsx} (85%) rename packages/ra-ui-materialui/src/layout/{Layout.js => Layout.tsx} (68%) rename packages/ra-ui-materialui/src/layout/{LinearProgress.js => LinearProgress.tsx} (85%) rename packages/ra-ui-materialui/src/layout/{Loading.js => Loading.tsx} (90%) rename packages/ra-ui-materialui/src/layout/{LoadingIndicator.js => LoadingIndicator.tsx} (71%) rename packages/ra-ui-materialui/src/layout/{NotFound.js => NotFound.tsx} (81%) rename packages/ra-ui-materialui/src/layout/{Sidebar.js => Sidebar.tsx} (79%) rename packages/ra-ui-materialui/src/layout/{Title.js => Title.tsx} (73%) delete mode 100644 packages/ra-ui-materialui/src/layout/TitleForRecord.js create mode 100644 packages/ra-ui-materialui/src/layout/TitleForRecord.tsx rename packages/ra-ui-materialui/src/layout/{TopToolbar.js => TopToolbar.tsx} (89%) rename packages/ra-ui-materialui/src/layout/{UserMenu.js => UserMenu.tsx} (88%) rename packages/ra-ui-materialui/src/layout/{index.js => index.ts} (100%) diff --git a/packages/ra-ui-materialui/src/defaultTheme.ts b/packages/ra-ui-materialui/src/defaultTheme.ts index dc9a3ed0aa4..b87d794b3e0 100644 --- a/packages/ra-ui-materialui/src/defaultTheme.ts +++ b/packages/ra-ui-materialui/src/defaultTheme.ts @@ -1,4 +1,24 @@ -export default { +import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme'; +import { CSSProperties } from 'react'; + +type Width = CSSProperties['width']; + +declare module '@material-ui/core/styles/createMuiTheme' { + interface Theme { + sidebar: { + width: Width; + closedWidth: Width; + }; + } + interface ThemeOptions { + sidebar: { + width: Width; + closedWidth: Width; + }; + } +} + +const defaultTheme: ThemeOptions = { palette: { secondary: { light: '#6ec6ff', @@ -7,11 +27,6 @@ export default { contrastText: '#fff', }, }, - typography: { - title: { - fontWeight: 400, - }, - }, sidebar: { width: 240, closedWidth: 55, @@ -27,3 +42,5 @@ export default { }, }, }; + +export default defaultTheme; diff --git a/packages/ra-ui-materialui/src/layout/CardContentInner.js b/packages/ra-ui-materialui/src/layout/CardContentInner.tsx similarity index 83% rename from packages/ra-ui-materialui/src/layout/CardContentInner.js rename to packages/ra-ui-materialui/src/layout/CardContentInner.tsx index 573c85cab2b..e0d19b26cd7 100644 --- a/packages/ra-ui-materialui/src/layout/CardContentInner.js +++ b/packages/ra-ui-materialui/src/layout/CardContentInner.tsx @@ -1,7 +1,7 @@ -import React from 'react'; +import React, { FC } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import CardContent from '@material-ui/core/CardContent'; +import CardContent, { CardContentProps } from '@material-ui/core/CardContent'; import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles( @@ -30,11 +30,11 @@ const useStyles = makeStyles( * padding double the spacing between each CardContent, leading to too much * wasted space. Use this component as a CardContent alternative. */ -const CardContentInner = props => { - const { className, children } = props; +const CardContentInner: FC = props => { + const { className, children, ...rest } = props; const classes = useStyles(props); return ( - + {children} ); diff --git a/packages/ra-ui-materialui/src/layout/DeviceTestWrapper.js b/packages/ra-ui-materialui/src/layout/DeviceTestWrapper.tsx similarity index 82% rename from packages/ra-ui-materialui/src/layout/DeviceTestWrapper.js rename to packages/ra-ui-materialui/src/layout/DeviceTestWrapper.tsx index a8e0abd50b7..12db47b4185 100644 --- a/packages/ra-ui-materialui/src/layout/DeviceTestWrapper.js +++ b/packages/ra-ui-materialui/src/layout/DeviceTestWrapper.tsx @@ -1,7 +1,12 @@ -import React from 'react'; +import React, { ReactNode, FC } from 'react'; import mediaQuery from 'css-mediaquery'; import { ThemeProvider } from '@material-ui/styles'; import { createMuiTheme } from '@material-ui/core/styles'; +import { Breakpoint } from '@material-ui/core/styles/createBreakpoints'; + +interface Props { + width: Breakpoint; +} /** * Test utility to simulate a device form factor for server-side mediaQueries @@ -14,7 +19,7 @@ import { createMuiTheme } from '@material-ui/core/styles'; * * */ -const DeviceTestWrapper = ({ width = 'md', children }) => { +const DeviceTestWrapper: FC = ({ width = 'md', children }) => { const theme = createMuiTheme(); // Use https://github.com/ericf/css-mediaquery as ponyfill. diff --git a/packages/ra-ui-materialui/src/layout/Error.js b/packages/ra-ui-materialui/src/layout/Error.tsx similarity index 85% rename from packages/ra-ui-materialui/src/layout/Error.js rename to packages/ra-ui-materialui/src/layout/Error.tsx index 6f3c3301a9b..a3dec1b4b79 100644 --- a/packages/ra-ui-materialui/src/layout/Error.js +++ b/packages/ra-ui-materialui/src/layout/Error.tsx @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react'; +import React, { Fragment, FC, ErrorInfo } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import Button from '@material-ui/core/Button'; @@ -10,7 +10,7 @@ import ErrorIcon from '@material-ui/icons/Report'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import History from '@material-ui/icons/History'; -import Title, { TitlePropType } from './Title'; +import Title, { TitlePropType, TitleProps } from './Title'; import { useTranslate } from 'ra-core'; const useStyles = makeStyles( @@ -52,7 +52,15 @@ function goBack() { window.history.go(-1); } -const Error = props => { +export interface ErrorComponentProps { + className?: string; + error: Error; + errorInfo: ErrorInfo; + title: TitleProps['defaultTitle']; + classes?: object; +} + +const Error: FC = props => { const { error, errorInfo, @@ -86,12 +94,8 @@ const Error = props => { )}
-
@@ -102,8 +106,8 @@ const Error = props => { Error.propTypes = { classes: PropTypes.object, className: PropTypes.string, - error: PropTypes.object.isRequired, - errorInfo: PropTypes.object, + error: PropTypes.any.isRequired, + errorInfo: PropTypes.any, title: TitlePropType, }; diff --git a/packages/ra-ui-materialui/src/layout/Layout.js b/packages/ra-ui-materialui/src/layout/Layout.tsx similarity index 68% rename from packages/ra-ui-materialui/src/layout/Layout.js rename to packages/ra-ui-materialui/src/layout/Layout.tsx index b55a6152089..eb53240491f 100644 --- a/packages/ra-ui-materialui/src/layout/Layout.js +++ b/packages/ra-ui-materialui/src/layout/Layout.tsx @@ -4,26 +4,37 @@ import React, { useEffect, useRef, useState, + ComponentType, + ReactNode, + ErrorInfo, + FC, } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import classnames from 'classnames'; -import { withRouter } from 'react-router-dom'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; import { createMuiTheme, withStyles, createStyles, } from '@material-ui/core/styles'; -import { ThemeProvider } from '@material-ui/styles'; +import { ThemeProvider, ClassNameMap } from '@material-ui/styles'; import compose from 'recompose/compose'; import DefaultAppBar from './AppBar'; import DefaultSidebar from './Sidebar'; -import DefaultMenu from './Menu'; -import DefaultNotification from './Notification'; -import DefaultError from './Error'; +import DefaultMenu, { MenuProps } from './Menu'; +import DefaultNotification, { NotificationProps } from './Notification'; +import DefaultError, { ErrorComponentProps } from './Error'; import defaultTheme from '../defaultTheme'; -import { ComponentPropType } from 'ra-core'; +import { + ComponentPropType, + DashboardComponent, + TitleComponent, + ReduxState, +} from 'ra-core'; +import { DrawerProps } from '@material-ui/core'; +import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme'; const styles = theme => createStyles({ @@ -76,9 +87,54 @@ const sanitizeRestProps = ({ ...props }) => props; -class Layout extends Component { - state = { hasError: false, errorMessage: null, errorInfo: null }; +interface LayoutProps extends RouteComponentProps { + appBar?: ComponentType; + classes?: ClassNameMap; + className?: string; + customRoutes?: any[]; + dashboard?: DashboardComponent; + error?: ComponentType; + logout?: ReactNode; + menu?: ComponentType; + notification?: ComponentType; + open?: boolean; + sidebar?: ComponentType; + title: TitleComponent; +} +type LayoutState = { + hasError?: boolean; + errorMessage: Error | null; + errorInfo: ErrorInfo | null; +}; + +class Layout extends Component { + static propTypes = { + appBar: ComponentPropType, + children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), + classes: PropTypes.object, + className: PropTypes.string, + customRoutes: PropTypes.array, + dashboard: ComponentPropType, + error: ComponentPropType, + history: PropTypes.object.isRequired, + logout: PropTypes.element, + menu: ComponentPropType, + notification: ComponentPropType, + open: PropTypes.bool, + sidebar: ComponentPropType, + title: PropTypes.node.isRequired, + }; + + static defaultProps = { + appBar: DefaultAppBar, + error: DefaultError, + menu: DefaultMenu, + notification: DefaultNotification, + sidebar: DefaultSidebar, + }; + + state = { hasError: false, errorMessage: null, errorInfo: null }; constructor(props) { super(props); /** @@ -146,32 +202,7 @@ class Layout extends Component { } } -Layout.propTypes = { - appBar: ComponentPropType, - children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - classes: PropTypes.object, - className: PropTypes.string, - customRoutes: PropTypes.array, - dashboard: ComponentPropType, - error: ComponentPropType, - history: PropTypes.object.isRequired, - logout: PropTypes.element, - menu: ComponentPropType, - notification: ComponentPropType, - open: PropTypes.bool, - sidebar: ComponentPropType, - title: PropTypes.node.isRequired, -}; - -Layout.defaultProps = { - appBar: DefaultAppBar, - error: DefaultError, - menu: DefaultMenu, - notification: DefaultNotification, - sidebar: DefaultSidebar, -}; - -const mapStateToProps = state => ({ +const mapStateToProps = (state: ReduxState) => ({ open: state.admin.ui.sidebarOpen, }); @@ -184,7 +215,14 @@ const EnhancedLayout = compose( withStyles(styles, { name: 'RaLayout' }) )(Layout); -const LayoutWithTheme = ({ theme: themeOverride, ...props }) => { +interface LayoutWithThemeProps extends LayoutProps { + theme?: ThemeOptions; +} + +const LayoutWithTheme: FC = ({ + theme: themeOverride, + ...props +}) => { const themeProp = useRef(themeOverride); const [theme, setTheme] = useState(createMuiTheme(themeOverride)); @@ -203,7 +241,7 @@ const LayoutWithTheme = ({ theme: themeOverride, ...props }) => { }; LayoutWithTheme.propTypes = { - theme: PropTypes.object, + theme: PropTypes.any, }; LayoutWithTheme.defaultProps = { diff --git a/packages/ra-ui-materialui/src/layout/LinearProgress.js b/packages/ra-ui-materialui/src/layout/LinearProgress.tsx similarity index 85% rename from packages/ra-ui-materialui/src/layout/LinearProgress.js rename to packages/ra-ui-materialui/src/layout/LinearProgress.tsx index 2822b0883e0..e58be311037 100644 --- a/packages/ra-ui-materialui/src/layout/LinearProgress.js +++ b/packages/ra-ui-materialui/src/layout/LinearProgress.tsx @@ -1,5 +1,7 @@ -import React from 'react'; -import Progress from '@material-ui/core/LinearProgress'; +import React, { FC } from 'react'; +import Progress, { + LinearProgressProps, +} from '@material-ui/core/LinearProgress'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -24,7 +26,7 @@ const useStyles = makeStyles( * * @param {Object} classes CSS class names */ -const LinearProgress = props => { +const LinearProgress: FC = props => { const { classes: classesOverride, className, ...rest } = props; const classes = useStyles(props); return ( diff --git a/packages/ra-ui-materialui/src/layout/Loading.js b/packages/ra-ui-materialui/src/layout/Loading.tsx similarity index 90% rename from packages/ra-ui-materialui/src/layout/Loading.js rename to packages/ra-ui-materialui/src/layout/Loading.tsx index ac71d81dd3e..7b0353b44c2 100644 --- a/packages/ra-ui-materialui/src/layout/Loading.js +++ b/packages/ra-ui-materialui/src/layout/Loading.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { FC } from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -33,7 +33,13 @@ const useStyles = makeStyles( { name: 'RaLoading' } ); -const Loading = props => { +interface Props { + className?: string; + loadingPrimary?: string; + loadingSecondary?: string; +} + +const Loading: FC = props => { const { className, loadingPrimary = 'ra.page.loading', @@ -53,7 +59,6 @@ const Loading = props => { }; Loading.propTypes = { - classes: PropTypes.object, className: PropTypes.string, loadingPrimary: PropTypes.string, loadingSecondary: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/layout/LoadingIndicator.js b/packages/ra-ui-materialui/src/layout/LoadingIndicator.tsx similarity index 71% rename from packages/ra-ui-materialui/src/layout/LoadingIndicator.js rename to packages/ra-ui-materialui/src/layout/LoadingIndicator.tsx index 0d999801e5c..02d1e5a21cb 100644 --- a/packages/ra-ui-materialui/src/layout/LoadingIndicator.js +++ b/packages/ra-ui-materialui/src/layout/LoadingIndicator.tsx @@ -1,10 +1,12 @@ -import React from 'react'; +import React, { FC } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; -import CircularProgress from '@material-ui/core/CircularProgress'; -import { useRefreshWhenVisible } from 'ra-core'; +import CircularProgress, { + CircularProgressProps, +} from '@material-ui/core/CircularProgress'; +import { useRefreshWhenVisible, ReduxState } from 'ra-core'; import RefreshIconButton from '../button/RefreshIconButton'; @@ -17,10 +19,10 @@ const useStyles = makeStyles( { name: 'RaLoadingIndicator' } ); -const LoadingIndicator = props => { +const LoadingIndicator: FC = props => { const { classes: classesOverride, className, ...rest } = props; useRefreshWhenVisible(); - const loading = useSelector(state => state.admin.loading > 0); + const loading = useSelector((state: ReduxState) => state.admin.loading > 0); const classes = useStyles(props); return loading ? ( { LoadingIndicator.propTypes = { classes: PropTypes.object, className: PropTypes.string, - width: PropTypes.string, + size: PropTypes.string, }; export default LoadingIndicator; diff --git a/packages/ra-ui-materialui/src/layout/Menu.tsx b/packages/ra-ui-materialui/src/layout/Menu.tsx index 7f7207f75f9..6ebf1b0ab2b 100644 --- a/packages/ra-ui-materialui/src/layout/Menu.tsx +++ b/packages/ra-ui-materialui/src/layout/Menu.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactElement } from 'react'; +import React, { FC, ReactElement, ReactNode } from 'react'; import PropTypes from 'prop-types'; import { shallowEqual, useSelector } from 'react-redux'; // @ts-ignore @@ -92,7 +92,7 @@ export interface MenuProps { className?: string; dense?: boolean; hasDashboard: boolean; - logout?: ReactElement; + logout?: ReactNode; onMenuClick?: () => void; } diff --git a/packages/ra-ui-materialui/src/layout/NotFound.js b/packages/ra-ui-materialui/src/layout/NotFound.tsx similarity index 81% rename from packages/ra-ui-materialui/src/layout/NotFound.js rename to packages/ra-ui-materialui/src/layout/NotFound.tsx index a3b5caccfb6..a7e662a088a 100644 --- a/packages/ra-ui-materialui/src/layout/NotFound.js +++ b/packages/ra-ui-materialui/src/layout/NotFound.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { FC } from 'react'; import PropTypes from 'prop-types'; import Button from '@material-ui/core/Button'; import { makeStyles } from '@material-ui/core/styles'; @@ -7,7 +7,8 @@ import History from '@material-ui/icons/History'; import classnames from 'classnames'; import { useAuthenticated, useTranslate } from 'ra-core'; -import Title from './Title'; +import Title, { TitleProps } from './Title'; +import { RouteComponentProps } from 'react-router-dom'; const useStyles = makeStyles( theme => ({ @@ -45,8 +46,14 @@ function goBack() { window.history.go(-1); } -const NotFound = props => { - const { className, classes: classesOverride, title, ...rest } = props; +interface Props extends RouteComponentProps { + className: string; + title: TitleProps['defaultTitle']; + classes?: object; +} + +const NotFound: FC = props => { + const { className, title, ...rest } = props; const classes = useStyles(props); const translate = useTranslate(); useAuthenticated(); @@ -62,8 +69,8 @@ const NotFound = props => {
{translate('ra.message.not_found')}.
-
@@ -82,7 +89,6 @@ NotFound.propTypes = { className: PropTypes.string, classes: PropTypes.object, title: PropTypes.string, - location: PropTypes.object, }; export default NotFound; diff --git a/packages/ra-ui-materialui/src/layout/Notification.tsx b/packages/ra-ui-materialui/src/layout/Notification.tsx index 3e8aeb2d0c5..289a92b0fbe 100644 --- a/packages/ra-ui-materialui/src/layout/Notification.tsx +++ b/packages/ra-ui-materialui/src/layout/Notification.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, FC } from 'react'; import PropTypes from 'prop-types'; import { useSelector, useDispatch } from 'react-redux'; import Snackbar, { SnackbarProps } from '@material-ui/core/Snackbar'; @@ -15,7 +15,7 @@ import { useTranslate, } from 'ra-core'; -interface Props { +export interface NotificationProps extends Omit { type?: string; } @@ -36,9 +36,7 @@ const useStyles = makeStyles( { name: 'RaNotification' } ); -const Notification: React.FunctionComponent< - Props & Omit -> = props => { +const Notification: FC = props => { const { classes: classesOverride, type, diff --git a/packages/ra-ui-materialui/src/layout/Sidebar.js b/packages/ra-ui-materialui/src/layout/Sidebar.tsx similarity index 79% rename from packages/ra-ui-materialui/src/layout/Sidebar.js rename to packages/ra-ui-materialui/src/layout/Sidebar.tsx index 9898de49f5a..f7957ac7804 100644 --- a/packages/ra-ui-materialui/src/layout/Sidebar.js +++ b/packages/ra-ui-materialui/src/layout/Sidebar.tsx @@ -1,14 +1,24 @@ -import React, { Children, cloneElement } from 'react'; +import React, { Children, cloneElement, FC } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; -import { Drawer, makeStyles, useMediaQuery } from '@material-ui/core'; +import { + Drawer, + makeStyles, + useMediaQuery, + Theme, + DrawerProps, +} from '@material-ui/core'; import lodashGet from 'lodash/get'; -import { setSidebarVisibility } from 'ra-core'; +import { setSidebarVisibility, ReduxState } from 'ra-core'; export const DRAWER_WIDTH = 240; export const CLOSED_DRAWER_WIDTH = 55; -const useStyles = makeStyles( +interface StyleProps { + open: boolean; +} + +const useStyles = makeStyles( theme => ({ drawerPaper: { position: 'relative', @@ -45,22 +55,17 @@ const useStyles = makeStyles( { name: 'RaSidebar' } ); -const Sidebar = props => { - const { - children, - closedSize, - size, - classes: classesOverride, - ...rest - } = props; +const Sidebar: FC = props => { + const { children, ...rest } = props; const dispatch = useDispatch(); - const isXSmall = useMediaQuery(theme => theme.breakpoints.down('xs')); - const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); - const open = useSelector(state => state.admin.ui.sidebarOpen); - useSelector(state => state.locale); // force redraw on locale change + const isXSmall = useMediaQuery(theme => + theme.breakpoints.down('xs') + ); + const isSmall = useMediaQuery(theme => theme.breakpoints.down('sm')); + const open = useSelector((state: ReduxState) => state.admin.ui.sidebarOpen); const handleClose = () => dispatch(setSidebarVisibility(false)); const toggleSidebar = () => dispatch(setSidebarVisibility(!open)); - const classes = useStyles({ ...props, open }); + const classes = useStyles({ open }); return isXSmall ? ( { +export interface TitleProps { + defaultTitle: TitleComponent; + className?: string; + record?: any; + title?: TitleComponent; +} + +const Title: FC = ({ + className, + defaultTitle, + record, + title, + ...rest +}) => { const translate = useTranslate(); const container = typeof document !== 'undefined' @@ -34,7 +47,6 @@ export const TitlePropType = PropTypes.oneOfType([ Title.propTypes = { defaultTitle: PropTypes.string, className: PropTypes.string, - locale: PropTypes.string, record: PropTypes.object, title: TitlePropType, }; diff --git a/packages/ra-ui-materialui/src/layout/TitleForRecord.js b/packages/ra-ui-materialui/src/layout/TitleForRecord.js deleted file mode 100644 index 96f265c76a9..00000000000 --- a/packages/ra-ui-materialui/src/layout/TitleForRecord.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Title, { TitlePropType } from './Title'; - -const TitleForRecord = ({ defaultTitle, record, title }) => - record ? ( - - ) : ( - '' - ); - -TitleForRecord.propTypes = { - defaultTitle: PropTypes.any, - record: PropTypes.object, - title: TitlePropType, -}; - -export default TitleForRecord; diff --git a/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx new file mode 100644 index 00000000000..ad8d13deb4e --- /dev/null +++ b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; +import PropTypes from 'prop-types'; +import Title, { TitlePropType, TitleProps } from './Title'; + +interface Props extends TitleProps { + record: TitleProps['record']; +} + +const TitleForRecord: FC<Props> = props => + props.record ? <Title {...props} /> : null; + +TitleForRecord.propTypes = { + defaultTitle: PropTypes.any, + record: PropTypes.object, + title: TitlePropType, +}; + +export default TitleForRecord; diff --git a/packages/ra-ui-materialui/src/layout/TopToolbar.js b/packages/ra-ui-materialui/src/layout/TopToolbar.tsx similarity index 89% rename from packages/ra-ui-materialui/src/layout/TopToolbar.js rename to packages/ra-ui-materialui/src/layout/TopToolbar.tsx index 66f5a96c9a5..f52124e72e8 100644 --- a/packages/ra-ui-materialui/src/layout/TopToolbar.js +++ b/packages/ra-ui-materialui/src/layout/TopToolbar.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { FC } from 'react'; import PropTypes from 'prop-types'; -import Toolbar from '@material-ui/core/Toolbar'; +import Toolbar, { ToolbarProps } from '@material-ui/core/Toolbar'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; @@ -29,7 +29,7 @@ const useStyles = makeStyles( { name: 'RaTopToolbar' } ); -const TopToolbar = props => { +const TopToolbar: FC<ToolbarProps> = props => { const { className, children, ...rest } = props; const classes = useStyles(props); return ( diff --git a/packages/ra-ui-materialui/src/layout/UserMenu.js b/packages/ra-ui-materialui/src/layout/UserMenu.tsx similarity index 88% rename from packages/ra-ui-materialui/src/layout/UserMenu.js rename to packages/ra-ui-materialui/src/layout/UserMenu.tsx index 25a39d38e1f..3ffb2b605bb 100644 --- a/packages/ra-ui-materialui/src/layout/UserMenu.js +++ b/packages/ra-ui-materialui/src/layout/UserMenu.tsx @@ -1,4 +1,11 @@ -import React, { Children, cloneElement, isValidElement, useState } from 'react'; +import React, { + Children, + cloneElement, + isValidElement, + useState, + FC, + ReactNode, +} from 'react'; import PropTypes from 'prop-types'; import { useTranslate } from 'ra-core'; import Tooltip from '@material-ui/core/Tooltip'; @@ -6,7 +13,13 @@ import IconButton from '@material-ui/core/IconButton'; import Menu from '@material-ui/core/Menu'; import AccountCircle from '@material-ui/icons/AccountCircle'; -const UserMenu = props => { +interface Props { + label?: string; + logout?: ReactNode; + icon?: ReactNode; +} + +const UserMenu: FC<Props> = props => { const [anchorEl, setAnchorEl] = useState(null); const translate = useTranslate(); @@ -59,7 +72,7 @@ const UserMenu = props => { UserMenu.propTypes = { children: PropTypes.node, - label: PropTypes.string.isRequired, + label: PropTypes.string, logout: PropTypes.element, icon: PropTypes.node, }; diff --git a/packages/ra-ui-materialui/src/layout/index.js b/packages/ra-ui-materialui/src/layout/index.ts similarity index 100% rename from packages/ra-ui-materialui/src/layout/index.js rename to packages/ra-ui-materialui/src/layout/index.ts From 11ac6514a1aea20b6fce91634a5a255e3b298c26 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Fri, 29 May 2020 16:07:30 +0200 Subject: [PATCH 2/7] fix failling checks --- packages/ra-ui-materialui/src/defaultTheme.ts | 4 ++-- packages/ra-ui-materialui/src/layout/Layout.tsx | 5 ++++- packages/ra-ui-materialui/src/layout/NotFound.tsx | 5 ++++- packages/ra-ui-materialui/src/layout/Sidebar.tsx | 10 +++++++--- packages/ra-ui-materialui/src/layout/UserMenu.tsx | 1 + 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/ra-ui-materialui/src/defaultTheme.ts b/packages/ra-ui-materialui/src/defaultTheme.ts index b87d794b3e0..c3601ce9659 100644 --- a/packages/ra-ui-materialui/src/defaultTheme.ts +++ b/packages/ra-ui-materialui/src/defaultTheme.ts @@ -5,13 +5,13 @@ type Width = CSSProperties['width']; declare module '@material-ui/core/styles/createMuiTheme' { interface Theme { - sidebar: { + sidebar?: { width: Width; closedWidth: Width; }; } interface ThemeOptions { - sidebar: { + sidebar?: { width: Width; closedWidth: Width; }; diff --git a/packages/ra-ui-materialui/src/layout/Layout.tsx b/packages/ra-ui-materialui/src/layout/Layout.tsx index eb53240491f..9954584ca6a 100644 --- a/packages/ra-ui-materialui/src/layout/Layout.tsx +++ b/packages/ra-ui-materialui/src/layout/Layout.tsx @@ -85,7 +85,10 @@ const sanitizeRestProps = ({ location, match, ...props -}) => props; +}: Partial<LayoutProps>): Omit< + LayoutProps, + keyof RouteComponentProps | 'title' +> => props; interface LayoutProps extends RouteComponentProps { appBar?: ComponentType<any>; diff --git a/packages/ra-ui-materialui/src/layout/NotFound.tsx b/packages/ra-ui-materialui/src/layout/NotFound.tsx index a7e662a088a..2cf1f75b860 100644 --- a/packages/ra-ui-materialui/src/layout/NotFound.tsx +++ b/packages/ra-ui-materialui/src/layout/NotFound.tsx @@ -83,7 +83,10 @@ const sanitizeRestProps = ({ location, match, ...rest -}) => rest; +}: Partial<Props>): Omit< + Props, + keyof RouteComponentProps | 'className' | 'title' +> => rest; NotFound.propTypes = { className: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/layout/Sidebar.tsx b/packages/ra-ui-materialui/src/layout/Sidebar.tsx index f7957ac7804..3cc3fa2cc05 100644 --- a/packages/ra-ui-materialui/src/layout/Sidebar.tsx +++ b/packages/ra-ui-materialui/src/layout/Sidebar.tsx @@ -1,4 +1,4 @@ -import React, { Children, cloneElement, FC } from 'react'; +import React, { Children, cloneElement, FC, ReactElement } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { @@ -55,7 +55,11 @@ const useStyles = makeStyles<Theme, StyleProps>( { name: 'RaSidebar' } ); -const Sidebar: FC<DrawerProps> = props => { +interface Props extends DrawerProps { + children: ReactElement; +} + +const Sidebar: FC<Props> = props => { const { children, ...rest } = props; const dispatch = useDispatch(); const isXSmall = useMediaQuery<Theme>(theme => @@ -111,7 +115,7 @@ const Sidebar: FC<DrawerProps> = props => { }; Sidebar.propTypes = { - children: PropTypes.node.isRequired, + children: PropTypes.element.isRequired, }; export default Sidebar; diff --git a/packages/ra-ui-materialui/src/layout/UserMenu.tsx b/packages/ra-ui-materialui/src/layout/UserMenu.tsx index 3ffb2b605bb..3c721511a27 100644 --- a/packages/ra-ui-materialui/src/layout/UserMenu.tsx +++ b/packages/ra-ui-materialui/src/layout/UserMenu.tsx @@ -17,6 +17,7 @@ interface Props { label?: string; logout?: ReactNode; icon?: ReactNode; + children?: ReactNode; } const UserMenu: FC<Props> = props => { From cf8e9e66d8576097be29b69b8edb718bb4f975c1 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Thu, 4 Jun 2020 10:28:29 +0200 Subject: [PATCH 3/7] revert CardContentInner changes --- .../src/layout/CardContentInner.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/ra-ui-materialui/src/layout/CardContentInner.tsx b/packages/ra-ui-materialui/src/layout/CardContentInner.tsx index e0d19b26cd7..9cce9a49b80 100644 --- a/packages/ra-ui-materialui/src/layout/CardContentInner.tsx +++ b/packages/ra-ui-materialui/src/layout/CardContentInner.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, ReactNode } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import CardContent, { CardContentProps } from '@material-ui/core/CardContent'; @@ -30,16 +30,22 @@ const useStyles = makeStyles( * padding double the spacing between each CardContent, leading to too much * wasted space. Use this component as a CardContent alternative. */ -const CardContentInner: FC<CardContentProps> = props => { - const { className, children, ...rest } = props; +const CardContentInner: FC<Props> = props => { + const { className, children } = props; const classes = useStyles(props); return ( - <CardContent className={classnames(classes.root, className)} {...rest}> + <CardContent className={classnames(classes.root, className)}> {children} </CardContent> ); }; +interface Props { + classes?: object; + className?: string; + children: ReactNode; +} + CardContentInner.propTypes = { className: PropTypes.string, classes: PropTypes.object, From fab3749bf16297e8e4d983928fcfbe1508258262 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Thu, 4 Jun 2020 10:42:24 +0200 Subject: [PATCH 4/7] startIcon prop for before Button children icon --- packages/ra-ui-materialui/src/layout/Confirm.tsx | 13 ++++++++++--- packages/ra-ui-materialui/src/layout/Error.tsx | 8 ++++++-- packages/ra-ui-materialui/src/layout/NotFound.tsx | 8 ++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/ra-ui-materialui/src/layout/Confirm.tsx b/packages/ra-ui-materialui/src/layout/Confirm.tsx index 8f8fd29c62e..689b8f4e00f 100644 --- a/packages/ra-ui-materialui/src/layout/Confirm.tsx +++ b/packages/ra-ui-materialui/src/layout/Confirm.tsx @@ -104,8 +104,13 @@ const Confirm: FC<ConfirmProps> = props => { </DialogContentText> </DialogContent> <DialogActions> - <Button disabled={loading} onClick={onClose}> - <CancelIcon className={classes.iconPaddingStyle} /> + <Button + disabled={loading} + onClick={onClose} + startIcon={ + <CancelIcon className={classes.iconPaddingStyle} /> + } + > {translate(cancel, { _: cancel })} </Button> <Button @@ -116,8 +121,10 @@ const Confirm: FC<ConfirmProps> = props => { [classes.confirmPrimary]: confirmColor === 'primary', })} autoFocus + startIcon={ + <ConfirmIcon className={classes.iconPaddingStyle} /> + } > - <ConfirmIcon className={classes.iconPaddingStyle} /> {translate(confirm, { _: confirm })} </Button> </DialogActions> diff --git a/packages/ra-ui-materialui/src/layout/Error.tsx b/packages/ra-ui-materialui/src/layout/Error.tsx index a3dec1b4b79..62bdb2b3c83 100644 --- a/packages/ra-ui-materialui/src/layout/Error.tsx +++ b/packages/ra-ui-materialui/src/layout/Error.tsx @@ -94,8 +94,12 @@ const Error: FC<ErrorComponentProps> = props => { </ExpansionPanel> )} <div className={classes.toolbar}> - <Button variant="contained" onClick={goBack}> - <History /> {translate('ra.action.back')} + <Button + variant="contained" + onClick={goBack} + startIcon={<History />} + > + {translate('ra.action.back')} </Button> </div> </div> diff --git a/packages/ra-ui-materialui/src/layout/NotFound.tsx b/packages/ra-ui-materialui/src/layout/NotFound.tsx index 2cf1f75b860..55e42d20483 100644 --- a/packages/ra-ui-materialui/src/layout/NotFound.tsx +++ b/packages/ra-ui-materialui/src/layout/NotFound.tsx @@ -69,8 +69,12 @@ const NotFound: FC<Props> = props => { <div>{translate('ra.message.not_found')}.</div> </div> <div className={classes.toolbar}> - <Button variant="contained" onClick={goBack}> - <History /> {translate('ra.action.back')} + <Button + variant="contained" + onClick={goBack} + startIcon={<History />} + > + {translate('ra.action.back')} </Button> </div> </div> From 4e2532f8c84a138e2373eac400a3ccb2e532c206 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Thu, 4 Jun 2020 11:01:05 +0200 Subject: [PATCH 5/7] move type definitions after components --- .../ra-ui-materialui/src/layout/Error.tsx | 18 ++++---- .../ra-ui-materialui/src/layout/Layout.tsx | 44 +++++++++---------- .../ra-ui-materialui/src/layout/Loading.tsx | 12 ++--- .../ra-ui-materialui/src/layout/NotFound.tsx | 12 ++--- .../src/layout/Notification.tsx | 8 ++-- .../ra-ui-materialui/src/layout/Sidebar.tsx | 10 ++--- .../ra-ui-materialui/src/layout/Title.tsx | 14 +++--- .../src/layout/TitleForRecord.tsx | 6 +-- .../ra-ui-materialui/src/layout/UserMenu.tsx | 15 +++---- 9 files changed, 69 insertions(+), 70 deletions(-) diff --git a/packages/ra-ui-materialui/src/layout/Error.tsx b/packages/ra-ui-materialui/src/layout/Error.tsx index 62bdb2b3c83..99fcb6555c5 100644 --- a/packages/ra-ui-materialui/src/layout/Error.tsx +++ b/packages/ra-ui-materialui/src/layout/Error.tsx @@ -52,15 +52,7 @@ function goBack() { window.history.go(-1); } -export interface ErrorComponentProps { - className?: string; - error: Error; - errorInfo: ErrorInfo; - title: TitleProps['defaultTitle']; - classes?: object; -} - -const Error: FC<ErrorComponentProps> = props => { +const Error: FC<ErrorProps> = props => { const { error, errorInfo, @@ -107,6 +99,14 @@ const Error: FC<ErrorComponentProps> = props => { ); }; +export interface ErrorProps { + className?: string; + error: Error; + errorInfo: ErrorInfo; + title: TitleProps['defaultTitle']; + classes?: object; +} + Error.propTypes = { classes: PropTypes.object, className: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/layout/Layout.tsx b/packages/ra-ui-materialui/src/layout/Layout.tsx index 9954584ca6a..19eee3f3232 100644 --- a/packages/ra-ui-materialui/src/layout/Layout.tsx +++ b/packages/ra-ui-materialui/src/layout/Layout.tsx @@ -25,7 +25,7 @@ import DefaultAppBar from './AppBar'; import DefaultSidebar from './Sidebar'; import DefaultMenu, { MenuProps } from './Menu'; import DefaultNotification, { NotificationProps } from './Notification'; -import DefaultError, { ErrorComponentProps } from './Error'; +import DefaultError, { ErrorProps } from './Error'; import defaultTheme from '../defaultTheme'; import { ComponentPropType, @@ -90,27 +90,6 @@ const sanitizeRestProps = ({ keyof RouteComponentProps | 'title' > => props; -interface LayoutProps extends RouteComponentProps { - appBar?: ComponentType<any>; - classes?: ClassNameMap; - className?: string; - customRoutes?: any[]; - dashboard?: DashboardComponent; - error?: ComponentType<ErrorComponentProps>; - logout?: ReactNode; - menu?: ComponentType<MenuProps>; - notification?: ComponentType<NotificationProps>; - open?: boolean; - sidebar?: ComponentType<DrawerProps>; - title: TitleComponent; -} - -type LayoutState = { - hasError?: boolean; - errorMessage: Error | null; - errorInfo: ErrorInfo | null; -}; - class Layout extends Component<LayoutProps, LayoutState> { static propTypes = { appBar: ComponentPropType, @@ -243,6 +222,27 @@ const LayoutWithTheme: FC<LayoutWithThemeProps> = ({ ); }; +interface LayoutProps extends RouteComponentProps { + appBar?: ComponentType<any>; + classes?: ClassNameMap; + className?: string; + customRoutes?: any[]; + dashboard?: DashboardComponent; + error?: ComponentType<ErrorProps>; + logout?: ReactNode; + menu?: ComponentType<MenuProps>; + notification?: ComponentType<NotificationProps>; + open?: boolean; + sidebar?: ComponentType<DrawerProps>; + title: TitleComponent; +} + +type LayoutState = { + hasError?: boolean; + errorMessage: Error | null; + errorInfo: ErrorInfo | null; +}; + LayoutWithTheme.propTypes = { theme: PropTypes.any, }; diff --git a/packages/ra-ui-materialui/src/layout/Loading.tsx b/packages/ra-ui-materialui/src/layout/Loading.tsx index 7b0353b44c2..26ed466c0d0 100644 --- a/packages/ra-ui-materialui/src/layout/Loading.tsx +++ b/packages/ra-ui-materialui/src/layout/Loading.tsx @@ -33,12 +33,6 @@ const useStyles = makeStyles( { name: 'RaLoading' } ); -interface Props { - className?: string; - loadingPrimary?: string; - loadingSecondary?: string; -} - const Loading: FC<Props> = props => { const { className, @@ -58,6 +52,12 @@ const Loading: FC<Props> = props => { ); }; +interface Props { + className?: string; + loadingPrimary?: string; + loadingSecondary?: string; +} + Loading.propTypes = { className: PropTypes.string, loadingPrimary: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/layout/NotFound.tsx b/packages/ra-ui-materialui/src/layout/NotFound.tsx index 55e42d20483..a1b1b36727c 100644 --- a/packages/ra-ui-materialui/src/layout/NotFound.tsx +++ b/packages/ra-ui-materialui/src/layout/NotFound.tsx @@ -46,12 +46,6 @@ function goBack() { window.history.go(-1); } -interface Props extends RouteComponentProps { - className: string; - title: TitleProps['defaultTitle']; - classes?: object; -} - const NotFound: FC<Props> = props => { const { className, title, ...rest } = props; const classes = useStyles(props); @@ -92,6 +86,12 @@ const sanitizeRestProps = ({ keyof RouteComponentProps | 'className' | 'title' > => rest; +interface Props extends RouteComponentProps { + className: string; + title: TitleProps['defaultTitle']; + classes?: object; +} + NotFound.propTypes = { className: PropTypes.string, classes: PropTypes.object, diff --git a/packages/ra-ui-materialui/src/layout/Notification.tsx b/packages/ra-ui-materialui/src/layout/Notification.tsx index 289a92b0fbe..551be4bc664 100644 --- a/packages/ra-ui-materialui/src/layout/Notification.tsx +++ b/packages/ra-ui-materialui/src/layout/Notification.tsx @@ -15,10 +15,6 @@ import { useTranslate, } from 'ra-core'; -export interface NotificationProps extends Omit<SnackbarProps, 'open'> { - type?: string; -} - const useStyles = makeStyles( (theme: Theme) => ({ error: { @@ -109,6 +105,10 @@ const Notification: FC<NotificationProps> = props => { ); }; +export interface NotificationProps extends Omit<SnackbarProps, 'open'> { + type?: string; +} + Notification.propTypes = { type: PropTypes.string, }; diff --git a/packages/ra-ui-materialui/src/layout/Sidebar.tsx b/packages/ra-ui-materialui/src/layout/Sidebar.tsx index 3cc3fa2cc05..fd023bafc82 100644 --- a/packages/ra-ui-materialui/src/layout/Sidebar.tsx +++ b/packages/ra-ui-materialui/src/layout/Sidebar.tsx @@ -55,11 +55,7 @@ const useStyles = makeStyles<Theme, StyleProps>( { name: 'RaSidebar' } ); -interface Props extends DrawerProps { - children: ReactElement; -} - -const Sidebar: FC<Props> = props => { +const Sidebar: FC<SidebarProps> = props => { const { children, ...rest } = props; const dispatch = useDispatch(); const isXSmall = useMediaQuery<Theme>(theme => @@ -114,6 +110,10 @@ const Sidebar: FC<Props> = props => { ); }; +interface SidebarProps extends DrawerProps { + children: ReactElement; +} + Sidebar.propTypes = { children: PropTypes.element.isRequired, }; diff --git a/packages/ra-ui-materialui/src/layout/Title.tsx b/packages/ra-ui-materialui/src/layout/Title.tsx index 73e144a53c0..485d9e2d12c 100644 --- a/packages/ra-ui-materialui/src/layout/Title.tsx +++ b/packages/ra-ui-materialui/src/layout/Title.tsx @@ -3,13 +3,6 @@ import { createPortal } from 'react-dom'; import PropTypes from 'prop-types'; import { useTranslate, warning, TitleComponent } from 'ra-core'; -export interface TitleProps { - defaultTitle: TitleComponent; - className?: string; - record?: any; - title?: TitleComponent; -} - const Title: FC<TitleProps> = ({ className, defaultTitle, @@ -39,6 +32,13 @@ const Title: FC<TitleProps> = ({ return createPortal(titleElement, container); }; +export interface TitleProps { + defaultTitle: TitleComponent; + className?: string; + record?: any; + title?: TitleComponent; +} + export const TitlePropType = PropTypes.oneOfType([ PropTypes.string, PropTypes.element, diff --git a/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx index ad8d13deb4e..9a260f2fd1f 100644 --- a/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx +++ b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx @@ -2,13 +2,13 @@ import React, { FC } from 'react'; import PropTypes from 'prop-types'; import Title, { TitlePropType, TitleProps } from './Title'; +const TitleForRecord: FC<Props> = props => + props.record ? <Title {...props} /> : null; + interface Props extends TitleProps { record: TitleProps['record']; } -const TitleForRecord: FC<Props> = props => - props.record ? <Title {...props} /> : null; - TitleForRecord.propTypes = { defaultTitle: PropTypes.any, record: PropTypes.object, diff --git a/packages/ra-ui-materialui/src/layout/UserMenu.tsx b/packages/ra-ui-materialui/src/layout/UserMenu.tsx index 3c721511a27..e5c26723db7 100644 --- a/packages/ra-ui-materialui/src/layout/UserMenu.tsx +++ b/packages/ra-ui-materialui/src/layout/UserMenu.tsx @@ -12,14 +12,6 @@ import Tooltip from '@material-ui/core/Tooltip'; import IconButton from '@material-ui/core/IconButton'; import Menu from '@material-ui/core/Menu'; import AccountCircle from '@material-ui/icons/AccountCircle'; - -interface Props { - label?: string; - logout?: ReactNode; - icon?: ReactNode; - children?: ReactNode; -} - const UserMenu: FC<Props> = props => { const [anchorEl, setAnchorEl] = useState(null); const translate = useTranslate(); @@ -71,6 +63,13 @@ const UserMenu: FC<Props> = props => { ); }; +interface Props { + label?: string; + logout?: ReactNode; + icon?: ReactNode; + children?: ReactNode; +} + UserMenu.propTypes = { children: PropTypes.node, label: PropTypes.string, From e244a9c30b5623159f65016b24918a9131cd2fe0 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Thu, 4 Jun 2020 12:36:48 +0200 Subject: [PATCH 6/7] fix review requested changes --- packages/ra-ui-materialui/src/layout/Loading.tsx | 2 ++ packages/ra-ui-materialui/src/layout/Menu.tsx | 2 +- .../ra-ui-materialui/src/layout/NotFound.tsx | 14 +++++++------- packages/ra-ui-materialui/src/layout/Sidebar.tsx | 2 +- .../src/layout/TitleForRecord.tsx | 16 ++++++++++++++-- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/ra-ui-materialui/src/layout/Loading.tsx b/packages/ra-ui-materialui/src/layout/Loading.tsx index 26ed466c0d0..96e2b9a232b 100644 --- a/packages/ra-ui-materialui/src/layout/Loading.tsx +++ b/packages/ra-ui-materialui/src/layout/Loading.tsx @@ -53,12 +53,14 @@ const Loading: FC<Props> = props => { }; interface Props { + classes?: object; className?: string; loadingPrimary?: string; loadingSecondary?: string; } Loading.propTypes = { + classes: PropTypes.object, className: PropTypes.string, loadingPrimary: PropTypes.string, loadingSecondary: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/layout/Menu.tsx b/packages/ra-ui-materialui/src/layout/Menu.tsx index 6ebf1b0ab2b..24bb6e17385 100644 --- a/packages/ra-ui-materialui/src/layout/Menu.tsx +++ b/packages/ra-ui-materialui/src/layout/Menu.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactElement, ReactNode } from 'react'; +import React, { FC, ReactNode } from 'react'; import PropTypes from 'prop-types'; import { shallowEqual, useSelector } from 'react-redux'; // @ts-ignore diff --git a/packages/ra-ui-materialui/src/layout/NotFound.tsx b/packages/ra-ui-materialui/src/layout/NotFound.tsx index a1b1b36727c..1f3de0c8054 100644 --- a/packages/ra-ui-materialui/src/layout/NotFound.tsx +++ b/packages/ra-ui-materialui/src/layout/NotFound.tsx @@ -46,8 +46,8 @@ function goBack() { window.history.go(-1); } -const NotFound: FC<Props> = props => { - const { className, title, ...rest } = props; +const NotFound: FC<NotFoundProps> = props => { + const { className, title, classes: classesOverride, ...rest } = props; const classes = useStyles(props); const translate = useTranslate(); useAuthenticated(); @@ -81,20 +81,20 @@ const sanitizeRestProps = ({ location, match, ...rest -}: Partial<Props>): Omit< - Props, +}: Partial<NotFoundProps>): Omit< + Partial<NotFoundProps>, keyof RouteComponentProps | 'className' | 'title' > => rest; -interface Props extends RouteComponentProps { +export interface NotFoundProps extends RouteComponentProps { className: string; title: TitleProps['defaultTitle']; - classes?: object; + classes?: ReturnType<typeof useStyles>; } NotFound.propTypes = { className: PropTypes.string, - classes: PropTypes.object, + classes: PropTypes.any, title: PropTypes.string, }; diff --git a/packages/ra-ui-materialui/src/layout/Sidebar.tsx b/packages/ra-ui-materialui/src/layout/Sidebar.tsx index fd023bafc82..e5d2f2bb7b1 100644 --- a/packages/ra-ui-materialui/src/layout/Sidebar.tsx +++ b/packages/ra-ui-materialui/src/layout/Sidebar.tsx @@ -65,7 +65,7 @@ const Sidebar: FC<SidebarProps> = props => { const open = useSelector((state: ReduxState) => state.admin.ui.sidebarOpen); const handleClose = () => dispatch(setSidebarVisibility(false)); const toggleSidebar = () => dispatch(setSidebarVisibility(!open)); - const classes = useStyles({ open }); + const classes = useStyles({ ...props, open }); return isXSmall ? ( <Drawer diff --git a/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx index 9a260f2fd1f..5793cee6ed8 100644 --- a/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx +++ b/packages/ra-ui-materialui/src/layout/TitleForRecord.tsx @@ -2,8 +2,20 @@ import React, { FC } from 'react'; import PropTypes from 'prop-types'; import Title, { TitlePropType, TitleProps } from './Title'; -const TitleForRecord: FC<Props> = props => - props.record ? <Title {...props} /> : null; +const TitleForRecord: FC<Props> = ({ + defaultTitle, + record, + title, + className, +}) => + record ? ( + <Title + title={title} + record={record} + defaultTitle={defaultTitle} + className={className} + /> + ) : null; interface Props extends TitleProps { record: TitleProps['record']; From 726c377a67f761c2e62dedd1790a7ef8c8f13119 Mon Sep 17 00:00:00 2001 From: Mohammed Faragallah <o0frego0o@hotmail.com> Date: Thu, 4 Jun 2020 13:56:55 +0200 Subject: [PATCH 7/7] sepereate ErrorBoundary from Layout --- .../src/layout/ErrorBoundary.tsx | 63 +++++ .../ra-ui-materialui/src/layout/Layout.tsx | 230 +++++++----------- 2 files changed, 152 insertions(+), 141 deletions(-) create mode 100644 packages/ra-ui-materialui/src/layout/ErrorBoundary.tsx diff --git a/packages/ra-ui-materialui/src/layout/ErrorBoundary.tsx b/packages/ra-ui-materialui/src/layout/ErrorBoundary.tsx new file mode 100644 index 00000000000..edcf4d14c41 --- /dev/null +++ b/packages/ra-ui-materialui/src/layout/ErrorBoundary.tsx @@ -0,0 +1,63 @@ +import { Component, ComponentType, createElement, ErrorInfo } from 'react'; +import PropTypes from 'prop-types'; +import { ComponentPropType, TitleComponent } from 'ra-core'; +import { withRouter, RouteComponentProps } from 'react-router'; + +import { ErrorProps } from './Error'; + +class _ErrorBoundary extends Component< + ErrorBoundaryProps & RouteComponentProps, + ErrorBoundaryState +> { + static propTypes = { + children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), + error: ComponentPropType, + title: PropTypes.element.isRequired, + }; + + state = { hasError: false, errorMessage: null, errorInfo: null }; + + constructor(props) { + super(props); + /** + * Reset the error state upon navigation + * + * @see https://stackoverflow.com/questions/48121750/browser-navigation-broken-by-use-of-react-error-boundaries + */ + props.history.listen(() => { + if (this.state.hasError) { + this.setState({ hasError: false }); + } + }); + } + + componentDidCatch(errorMessage, errorInfo) { + this.setState({ hasError: true, errorMessage, errorInfo }); + } + + render() { + const { hasError, errorMessage, errorInfo } = this.state; + const { children, error, title } = this.props; + + return hasError + ? createElement(error, { + error: errorMessage, + errorInfo, + title, + }) + : children; + } +} + +export const ErrorBoundary = withRouter(_ErrorBoundary); + +export interface ErrorBoundaryProps { + error: ComponentType<ErrorProps>; + title?: TitleComponent; +} + +interface ErrorBoundaryState { + hasError?: boolean; + errorMessage: Error | null; + errorInfo: ErrorInfo | null; +} diff --git a/packages/ra-ui-materialui/src/layout/Layout.tsx b/packages/ra-ui-materialui/src/layout/Layout.tsx index 19eee3f3232..57c2789d499 100644 --- a/packages/ra-ui-materialui/src/layout/Layout.tsx +++ b/packages/ra-ui-materialui/src/layout/Layout.tsx @@ -1,43 +1,33 @@ import React, { - Component, + ComponentType, createElement, + FC, + ReactElement, + ReactNode, useEffect, useRef, useState, - ComponentType, - ReactNode, - ErrorInfo, - FC, } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import { DrawerProps, makeStyles } from '@material-ui/core'; +import { createMuiTheme } from '@material-ui/core/styles'; +import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme'; +import { ThemeProvider } from '@material-ui/styles'; import classnames from 'classnames'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { - createMuiTheme, - withStyles, - createStyles, -} from '@material-ui/core/styles'; -import { ThemeProvider, ClassNameMap } from '@material-ui/styles'; -import compose from 'recompose/compose'; +import { ComponentPropType, DashboardComponent, ReduxState } from 'ra-core'; +import { useSelector } from 'react-redux'; +import { RouteComponentProps } from 'react-router-dom'; import DefaultAppBar from './AppBar'; import DefaultSidebar from './Sidebar'; import DefaultMenu, { MenuProps } from './Menu'; import DefaultNotification, { NotificationProps } from './Notification'; -import DefaultError, { ErrorProps } from './Error'; +import DefaultError from './Error'; import defaultTheme from '../defaultTheme'; -import { - ComponentPropType, - DashboardComponent, - TitleComponent, - ReduxState, -} from 'ra-core'; -import { DrawerProps } from '@material-ui/core'; -import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme'; +import { ErrorBoundary, ErrorBoundaryProps } from './ErrorBoundary'; -const styles = theme => - createStyles({ +const useStyles = makeStyles( + theme => ({ root: { display: 'flex', flexDirection: 'column', @@ -77,7 +67,9 @@ const styles = theme => padding: 0, }, }, - }); + }), + { name: 'RaLayout' } +); const sanitizeRestProps = ({ staticContext, @@ -86,116 +78,79 @@ const sanitizeRestProps = ({ match, ...props }: Partial<LayoutProps>): Omit< - LayoutProps, + Partial<LayoutProps>, keyof RouteComponentProps | 'title' > => props; -class Layout extends Component<LayoutProps, LayoutState> { - static propTypes = { - appBar: ComponentPropType, - children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - classes: PropTypes.object, - className: PropTypes.string, - customRoutes: PropTypes.array, - dashboard: ComponentPropType, - error: ComponentPropType, - history: PropTypes.object.isRequired, - logout: PropTypes.element, - menu: ComponentPropType, - notification: ComponentPropType, - open: PropTypes.bool, - sidebar: ComponentPropType, - title: PropTypes.node.isRequired, - }; - - static defaultProps = { - appBar: DefaultAppBar, - error: DefaultError, - menu: DefaultMenu, - notification: DefaultNotification, - sidebar: DefaultSidebar, - }; - - state = { hasError: false, errorMessage: null, errorInfo: null }; - constructor(props) { - super(props); - /** - * Reset the error state upon navigation - * - * @see https://stackoverflow.com/questions/48121750/browser-navigation-broken-by-use-of-react-error-boundaries - */ - props.history.listen(() => { - if (this.state.hasError) { - this.setState({ hasError: false }); - } - }); - } - - componentDidCatch(errorMessage, errorInfo) { - this.setState({ hasError: true, errorMessage, errorInfo }); - } - - render() { - const { - appBar, - children, - classes, - className, - customRoutes, - error, - dashboard, - logout, - menu, - notification, - open, - sidebar, - title, - ...props - } = this.props; - const { hasError, errorMessage, errorInfo } = this.state; - return ( - <div - className={classnames('layout', classes.root, className)} - {...sanitizeRestProps(props)} - > - <div className={classes.appFrame}> - {createElement(appBar, { title, open, logout })} - <main className={classes.contentWithSidebar}> - {createElement(sidebar, { - children: createElement(menu, { - logout, - hasDashboard: !!dashboard, - }), - })} - <div className={classes.content}> - {hasError - ? createElement(error, { - error: errorMessage, - errorInfo, - title, - }) - : children} - </div> - </main> - {createElement(notification)} - </div> +export const Layout: FC<LayoutProps> = props => { + const { + appBar, + children, + classes: classesOverride, + className, + customRoutes, + error, + dashboard, + logout, + menu, + notification, + sidebar, + title, + ...rest + } = props; + + const open = useSelector((state: ReduxState) => state.admin.ui.sidebarOpen); + const classes = useStyles(); + + return ( + <div + className={classnames('layout', classes.root, className)} + {...sanitizeRestProps(rest)} + > + <div className={classes.appFrame}> + {createElement(appBar, { title, open, logout })} + <main className={classes.contentWithSidebar}> + {createElement(sidebar, { + children: createElement(menu, { + logout, + hasDashboard: !!dashboard, + }), + })} + <div className={classes.content}> + <ErrorBoundary error={error} title={title}> + {children} + </ErrorBoundary> + </div> + </main> + {createElement(notification)} </div> - ); - } -} + </div> + ); +}; -const mapStateToProps = (state: ReduxState) => ({ - open: state.admin.ui.sidebarOpen, -}); +Layout.propTypes = { + appBar: ComponentPropType, + children: PropTypes.element, + classes: PropTypes.object, + className: PropTypes.string, + customRoutes: PropTypes.array, + dashboard: ComponentPropType, + error: ComponentPropType, + logout: PropTypes.element, + menu: ComponentPropType, + notification: ComponentPropType, + open: PropTypes.bool, + sidebar: ComponentPropType, + title: PropTypes.element.isRequired, +}; -const EnhancedLayout = compose( - connect( - mapStateToProps, - {} // Avoid connect passing dispatch in props - ), - withRouter, - withStyles(styles, { name: 'RaLayout' }) -)(Layout); +Layout.defaultProps = { + appBar: DefaultAppBar, + error: DefaultError, + menu: DefaultMenu, + notification: DefaultNotification, + sidebar: DefaultSidebar, +}; interface LayoutWithThemeProps extends LayoutProps { theme?: ThemeOptions; @@ -217,32 +172,25 @@ const LayoutWithTheme: FC<LayoutWithThemeProps> = ({ return ( <ThemeProvider theme={theme}> - <EnhancedLayout {...props} /> + <Layout {...props} /> </ThemeProvider> ); }; -interface LayoutProps extends RouteComponentProps { +interface LayoutProps extends RouteComponentProps, ErrorBoundaryProps { appBar?: ComponentType<any>; - classes?: ClassNameMap; + classes?: object; + children: ReactElement; className?: string; customRoutes?: any[]; dashboard?: DashboardComponent; - error?: ComponentType<ErrorProps>; logout?: ReactNode; menu?: ComponentType<MenuProps>; notification?: ComponentType<NotificationProps>; open?: boolean; sidebar?: ComponentType<DrawerProps>; - title: TitleComponent; } -type LayoutState = { - hasError?: boolean; - errorMessage: Error | null; - errorInfo: ErrorInfo | null; -}; - LayoutWithTheme.propTypes = { theme: PropTypes.any, };