diff --git a/examples/simple/src/comments/CommentEdit.js b/examples/simple/src/comments/CommentEdit.js index 645ce7fd52e..6aeff5f4be3 100644 --- a/examples/simple/src/comments/CommentEdit.js +++ b/examples/simple/src/comments/CommentEdit.js @@ -1,6 +1,6 @@ import Card from '@material-ui/core/Card'; import Typography from '@material-ui/core/Typography'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import React from 'react'; import { AutocompleteInput, @@ -25,7 +25,7 @@ const LinkToRelatedPost = ({ record }) => ( ); -const editStyles = { +const useEditStyles = makeStyles({ actions: { float: 'right', }, @@ -33,63 +33,69 @@ const editStyles = { marginTop: '1em', maxWidth: '30em', }, -}; +}); + +const CommentEdit = props => { + const classes = useEditStyles(); -const CommentEdit = withStyles(editStyles)(({ classes, ...props }) => ( - - {({ resource, record, redirect, save, basePath, version }) => ( -
- - <div className={classes.actions}> - <EditActions - basePath={basePath} - resource={resource} - data={record} - hasShow - hasList + return ( + <EditController {...props}> + {({ resource, record, redirect, save, basePath, version }) => ( + <div className="edit-page"> + <Title + defaultTitle={`Comment #${record ? record.id : ''}`} /> - </div> - <Card className={classes.card}> - {record && ( - <SimpleForm + <div className={classes.actions}> + <EditActions basePath={basePath} - redirect={redirect} resource={resource} - record={record} - save={save} - version={version} - > - <DisabledInput source="id" fullWidth /> - <ReferenceInput - source="post_id" - reference="posts" - perPage={15} - sort={{ field: 'title', order: 'ASC' }} - fullWidth + data={record} + hasShow + hasList + /> + </div> + <Card className={classes.card}> + {record && ( + <SimpleForm + basePath={basePath} + redirect={redirect} + resource={resource} + record={record} + save={save} + version={version} > - <AutocompleteInput - optionText="title" - options={{ fullWidth: true }} + <DisabledInput source="id" fullWidth /> + <ReferenceInput + source="post_id" + reference="posts" + perPage={15} + sort={{ field: 'title', order: 'ASC' }} + fullWidth + > + <AutocompleteInput + optionText="title" + options={{ fullWidth: true }} + /> + </ReferenceInput> + <LinkToRelatedPost /> + <TextInput + source="author.name" + validate={minLength(10)} + fullWidth /> - </ReferenceInput> - <LinkToRelatedPost /> - <TextInput - source="author.name" - validate={minLength(10)} - fullWidth - /> - <DateInput source="created_at" fullWidth /> - <LongTextInput - source="body" - validate={minLength(10)} - fullWidth - /> - </SimpleForm> - )} - </Card> - </div> - )} - </EditController> -)); + <DateInput source="created_at" fullWidth /> + <LongTextInput + source="body" + validate={minLength(10)} + fullWidth + /> + </SimpleForm> + )} + </Card> + </div> + )} + </EditController> + ); +}; export default CommentEdit; diff --git a/examples/simple/src/comments/CommentList.js b/examples/simple/src/comments/CommentList.js index c92aacb7b7d..ad049c8a2ed 100644 --- a/examples/simple/src/comments/CommentList.js +++ b/examples/simple/src/comments/CommentList.js @@ -10,7 +10,7 @@ import CardContent from '@material-ui/core/CardContent'; import CardHeader from '@material-ui/core/CardHeader'; import Grid from '@material-ui/core/Grid'; import Toolbar from '@material-ui/core/Toolbar'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import { unparse as convertToCSV } from 'papaparse/papaparse.min'; import { DateField, @@ -101,7 +101,7 @@ const CommentPagination = ({ ); }; -const listStyles = theme => ({ +const useListStyles = makeStyles(theme => ({ card: { height: '100%', display: 'flex', @@ -118,73 +118,73 @@ const listStyles = theme => ({ cardActions: { justifyContent: 'flex-end', }, -}); +})); -const CommentGrid = withStyles(listStyles)( - ({ classes, ids, data, basePath }) => { - const translate = useTranslate(); - return ( - <Grid spacing={2} container> - {ids.map(id => ( - <Grid item key={id} sm={12} md={6} lg={4}> - <Card className={classes.card}> - <CardHeader - className="comment" - title={ - <TextField - record={data[id]} - source="author.name" - /> - } - subheader={ - <DateField - record={data[id]} - source="created_at" - /> - } - avatar={ - <Avatar> - <PersonIcon /> - </Avatar> - } - /> - <CardContent className={classes.cardContent}> - <TextField record={data[id]} source="body" /> - </CardContent> - <CardContent className={classes.cardLink}> - {translate('comment.list.about')}  - <ReferenceField - resource="comments" - record={data[id]} - source="post_id" - reference="posts" - basePath={basePath} - > - <TextField - source="title" - className={classes.cardLinkLink} - /> - </ReferenceField> - </CardContent> - <CardActions className={classes.cardActions}> - <EditButton - resource="posts" - basePath={basePath} +const CommentGrid = ({ ids, data, basePath }) => { + const translate = useTranslate(); + const classes = useListStyles(); + + return ( + <Grid spacing={2} container> + {ids.map(id => ( + <Grid item key={id} sm={12} md={6} lg={4}> + <Card className={classes.card}> + <CardHeader + className="comment" + title={ + <TextField record={data[id]} + source="author.name" /> - <ShowButton - resource="posts" - basePath={basePath} + } + subheader={ + <DateField record={data[id]} + source="created_at" /> - </CardActions> - </Card> - </Grid> - ))} - </Grid> - ); - } -); + } + avatar={ + <Avatar> + <PersonIcon /> + </Avatar> + } + /> + <CardContent className={classes.cardContent}> + <TextField record={data[id]} source="body" /> + </CardContent> + <CardContent className={classes.cardLink}> + {translate('comment.list.about')}  + <ReferenceField + resource="comments" + record={data[id]} + source="post_id" + reference="posts" + basePath={basePath} + > + <TextField + source="title" + className={classes.cardLinkLink} + /> + </ReferenceField> + </CardContent> + <CardActions className={classes.cardActions}> + <EditButton + resource="posts" + basePath={basePath} + record={data[id]} + /> + <ShowButton + resource="posts" + basePath={basePath} + record={data[id]} + /> + </CardActions> + </Card> + </Grid> + ))} + </Grid> + ); +}; CommentGrid.defaultProps = { data: {}, diff --git a/examples/simple/src/comments/PostPreview.js b/examples/simple/src/comments/PostPreview.js index f14f4519bf0..2063f2a9a40 100644 --- a/examples/simple/src/comments/PostPreview.js +++ b/examples/simple/src/comments/PostPreview.js @@ -1,24 +1,25 @@ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector } from 'react-redux'; import { SimpleShowLayout, TextField } from 'react-admin'; -const PostPreviewView = ({ isLoading, ...props }) => ( - <SimpleShowLayout {...props}> - <TextField source="id" /> - <TextField source="title" /> - <TextField source="teaser" /> - </SimpleShowLayout> -); +const PostPreview = props => { + const record = useSelector( + state => + state.admin.resources[props.resource] + ? state.admin.resources[props.resource].data[props.id] + : null, + [props.resource, props.id], + ); + const version = useSelector(state => state.admin.ui.viewVersion); + const isLoading = useSelector(state => state.admin.loading > 0); -const mapStateToProps = (state, props) => ({ - record: state.admin.resources[props.resource] - ? state.admin.resources[props.resource].data[props.id] - : null, - isLoading: state.admin.loading > 0, - version: state.admin.ui.viewVersion, -}); + return ( + <SimpleShowLayout version={version} record={record} {...props}> + <TextField source="id" /> + <TextField source="title" /> + <TextField source="teaser" /> + </SimpleShowLayout> + ); +}; -export default connect( - mapStateToProps, - {} -)(PostPreviewView); +export default PostPreview; diff --git a/examples/simple/src/comments/PostQuickCreate.js b/examples/simple/src/comments/PostQuickCreate.js index a7d3df33e7c..fa7cea21f4d 100644 --- a/examples/simple/src/comments/PostQuickCreate.js +++ b/examples/simple/src/comments/PostQuickCreate.js @@ -1,7 +1,7 @@ -import React, { Component } from 'react'; +import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; +import { useSelector, useDispatch } from 'react-redux'; +import { makeStyles } from '@material-ui/core/styles'; import { CREATE, LongTextInput, @@ -10,6 +10,7 @@ import { TextInput, Toolbar, required, + showNotification, } from 'react-admin'; // eslint-disable-line import/no-unresolved import CancelButton from './PostQuickCreateCancelButton'; @@ -28,65 +29,64 @@ PostQuickCreateToolbar.propTypes = { onCancel: PropTypes.func.isRequired, }; -const styles = { +const useStyles = makeStyles({ form: { padding: 0 }, -}; +}); -class PostQuickCreateView extends Component { - static propTypes = { - dispatch: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - submitting: PropTypes.bool.isRequired, - }; +const PostQuickCreate = ({ onCancel, onSave }) => { + const classes = useStyles(); + const dispatch = useDispatch(); + const submitting = useSelector(state => state.admin.loading > 0); - handleSave = values => { - const { dispatch, onSave } = this.props; - dispatch({ - type: 'QUICK_CREATE', - payload: { data: values }, - meta: { - fetch: CREATE, - resource: 'posts', - onSuccess: { - callback: ({ payload: { data } }) => onSave(data), - }, - onError: { - callback: ({ error }) => this.setState({ error }), + const handleSave = useCallback( + values => { + dispatch({ + type: 'QUICK_CREATE', + payload: { data: values }, + meta: { + fetch: CREATE, + resource: 'posts', + onSuccess: { + callback: ({ payload: { data } }) => onSave(data), + }, + onError: { + callback: ({ error }) => { + dispatch(showNotification(error.message, 'error')); + }, + }, }, - }, - }); - }; + }); + }, + [onSave], + ); - render() { - const { classes, submitting, onCancel } = this.props; - - return ( - <SimpleForm - form="post-create" - save={this.handleSave} - saving={submitting} - redirect={false} - toolbar={props => ( + return ( + <SimpleForm + form="post-create" + save={handleSave} + saving={submitting} + redirect={false} + toolbar={useMemo( + () => props => ( <PostQuickCreateToolbar onCancel={onCancel} submitting={submitting} {...props} /> - )} - classes={{ form: classes.form }} - > - <TextInput source="title" validate={required()} /> - <LongTextInput source="teaser" validate={required()} /> - </SimpleForm> - ); - } -} + ), + [onCancel, submitting], + )} + classes={{ form: classes.form }} + > + <TextInput source="title" validate={required()} /> + <LongTextInput source="teaser" validate={required()} /> + </SimpleForm> + ); +}; -const mapStateToProps = state => ({ - submitting: state.admin.loading > 0, -}); +PostQuickCreate.propTypes = { + onCancel: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired, +}; -export default connect(mapStateToProps)( - withStyles(styles)(PostQuickCreateView) -); +export default PostQuickCreate; diff --git a/examples/simple/src/comments/PostQuickCreateCancelButton.js b/examples/simple/src/comments/PostQuickCreateCancelButton.js index f8c3b306946..9fc96582223 100644 --- a/examples/simple/src/comments/PostQuickCreateCancelButton.js +++ b/examples/simple/src/comments/PostQuickCreateCancelButton.js @@ -3,11 +3,11 @@ import PropTypes from 'prop-types'; import Button from '@material-ui/core/Button'; import IconCancel from '@material-ui/icons/Cancel'; -import withStyles from '@material-ui/core/styles/withStyles'; +import { makeStyles } from '@material-ui/core/styles'; import { useTranslate } from 'react-admin'; -const styles = { +const useStyles = makeStyles({ button: { margin: '10px 24px', position: 'relative', @@ -15,10 +15,14 @@ const styles = { iconPaddingStyle: { paddingRight: '0.5em', }, -}; +}); -const CancelButtonView = ({ classes, onClick, label = 'ra.action.cancel' }) => { +const PostQuickCreateCancelButton = ({ + onClick, + label = 'ra.action.cancel', +}) => { const translate = useTranslate(); + const classes = useStyles(); return ( <Button className={classes.button} onClick={onClick}> <IconCancel className={classes.iconPaddingStyle} /> @@ -27,10 +31,9 @@ const CancelButtonView = ({ classes, onClick, label = 'ra.action.cancel' }) => { ); }; -CancelButtonView.propTypes = { - classes: PropTypes.object, +PostQuickCreateCancelButton.propTypes = { label: PropTypes.string, onClick: PropTypes.func.isRequired, }; -export default withStyles(styles)(CancelButtonView); +export default PostQuickCreateCancelButton; diff --git a/examples/simple/src/comments/PostReferenceInput.js b/examples/simple/src/comments/PostReferenceInput.js index b6507f8903a..ce4a1122b06 100644 --- a/examples/simple/src/comments/PostReferenceInput.js +++ b/examples/simple/src/comments/PostReferenceInput.js @@ -1,10 +1,8 @@ -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import compose from 'recompose/compose'; +import React, { Fragment, useState, useCallback, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; import { Field } from 'redux-form'; -import withStyles from '@material-ui/core/styles/withStyles'; +import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import DialogTitle from '@material-ui/core/DialogTitle'; @@ -15,156 +13,142 @@ import { crudGetMatching, ReferenceInput, SelectInput, - translate, + useTranslate, } from 'react-admin'; // eslint-disable-line import/no-unresolved import PostQuickCreate from './PostQuickCreate'; import PostPreview from './PostPreview'; -const styles = { +const useStyles = makeStyles({ button: { margin: '10px 24px', position: 'relative', }, -}; +}); -class PostReferenceInputView extends React.Component { - static propTypes = { - classes: PropTypes.object.isRequired, - crudGetMatching: PropTypes.func.isRequired, - post_id: PropTypes.any, - translate: PropTypes.func.isRequired, - }; +const PostReferenceInput = props => { + const translate = useTranslate(); + const classes = useStyles(); + const dispatch = useDispatch(); - state = { - showCreateDialog: false, - showPreviewDialog: false, - new_post_id: '', - }; + const [showCreateDialog, setShowCreateDialog] = useState(false); + const [showPreviewDialog, setShowPreviewDialog] = useState(false); + const [newPostId, setNewPostId] = useState(''); - handleNewClick = event => { - event.preventDefault(); - this.setState({ showCreateDialog: true }); - }; + useEffect( + () => { + //Refresh the choices of the ReferenceInput to ensure our newly created post + // always appear, even after selecting another post + dispatch( + crudGetMatching( + 'posts', + 'comments@post_id', + { page: 1, perPage: 25 }, + { field: 'id', order: 'DESC' }, + {}, + ), + ); + }, + [newPostId], + ); - handleShowClick = event => { + const handleNewClick = useCallback(event => { event.preventDefault(); - this.setState({ showPreviewDialog: true }); - }; - - handleCloseCreate = () => { - this.setState({ showCreateDialog: false }); - }; + setShowCreateDialog(true); + }, []); - handleCloseShow = () => { - this.setState({ showPreviewDialog: false }); - }; + const handleShowClick = useCallback(event => { + event.preventDefault(); + setShowPreviewDialog(true); + }, []); - handleSave = post => { - const { crudGetMatching } = this.props; - this.setState({ showCreateDialog: false, new_post_id: post.id }, () => { - // Refresh the choices of the ReferenceInput to ensure our newly created post - // always appear, even after selecting another post - crudGetMatching( - 'posts', - 'comments@post_id', - { page: 1, perPage: 25 }, - { field: 'id', order: 'DESC' }, - {} - ); - }); - }; + const handleCloseCreate = useCallback(() => { + setShowCreateDialog(false); + }, []); - render() { - const { showCreateDialog, showPreviewDialog, new_post_id } = this.state; - const { classes, translate, ...props } = this.props; + const handleCloseShow = useCallback(() => { + setShowPreviewDialog(false); + }, []); - return ( - <Fragment> - <ReferenceInput {...props} defaultValue={new_post_id}> - <SelectInput optionText="title" /> - </ReferenceInput> - <Button - data-testid="button-add-post" - className={classes.button} - onClick={this.handleNewClick} - > - {translate('ra.action.create')} - </Button> - <Field - name="post_id" - component={({ input }) => - input.value && ( - <Fragment> - <Button - data-testid="button-show-post" - className={classes.button} - onClick={this.handleShowClick} - > - {translate('ra.action.show')} - </Button> - <Dialog - data-testid="dialog-show-post" - fullWidth - open={showPreviewDialog} - onClose={this.handleCloseShow} - aria-label={translate('simple.create-post')} - > - <DialogTitle> - {translate('simple.create-post')} - </DialogTitle> - <DialogContent> - <PostPreview - id={input.value} - basePath="/posts" - resource="posts" - /> - </DialogContent> - <DialogActions> - <Button - data-testid="button-close-modal" - onClick={this.handleCloseShow} - > - {translate('simple.action.close')} - </Button> - </DialogActions> - </Dialog> - </Fragment> - ) - } - /> - <Dialog - data-testid="dialog-add-post" - fullWidth - open={showCreateDialog} - onClose={this.handleCloseCreate} - aria-label={translate('simple.create-post')} - > - <DialogTitle>{translate('simple.create-post')}</DialogTitle> - <DialogContent> - <PostQuickCreate - onCancel={this.handleCloseCreate} - onSave={this.handleSave} - basePath="/posts" - form="post-create" - resource="posts" - /> - </DialogContent> - </Dialog> - </Fragment> - ); - } -} + const handleSave = useCallback(post => { + setShowCreateDialog(false); + setNewPostId(post.id); + }, []); -const mapDispatchToProps = { - crudGetMatching, + return ( + <Fragment> + <ReferenceInput {...props} defaultValue={newPostId}> + <SelectInput optionText="title" /> + </ReferenceInput> + <Button + data-testid="button-add-post" + className={classes.button} + onClick={handleNewClick} + > + {translate('ra.action.create')} + </Button> + <Field + name="post_id" + component={({ input }) => + input.value && ( + <Fragment> + <Button + data-testid="button-show-post" + className={classes.button} + onClick={handleShowClick} + > + {translate('ra.action.show')} + </Button> + <Dialog + data-testid="dialog-show-post" + fullWidth + open={showPreviewDialog} + onClose={handleCloseShow} + aria-label={translate('simple.create-post')} + > + <DialogTitle> + {translate('simple.create-post')} + </DialogTitle> + <DialogContent> + <PostPreview + id={input.value} + basePath="/posts" + resource="posts" + /> + </DialogContent> + <DialogActions> + <Button + data-testid="button-close-modal" + onClick={handleCloseShow} + > + {translate('simple.action.close')} + </Button> + </DialogActions> + </Dialog> + </Fragment> + ) + } + /> + <Dialog + data-testid="dialog-add-post" + fullWidth + open={showCreateDialog} + onClose={handleCloseCreate} + aria-label={translate('simple.create-post')} + > + <DialogTitle>{translate('simple.create-post')}</DialogTitle> + <DialogContent> + <PostQuickCreate + onCancel={handleCloseCreate} + onSave={handleSave} + basePath="/posts" + form="post-create" + resource="posts" + /> + </DialogContent> + </Dialog> + </Fragment> + ); }; -export default compose( - connect( - undefined, - mapDispatchToProps - ), - withStyles(styles), - translate -)(PostReferenceInputView); +export default PostReferenceInput; diff --git a/examples/simple/src/posts/PostCreate.js b/examples/simple/src/posts/PostCreate.js index 9cf6601bc57..b0acfaebfb1 100644 --- a/examples/simple/src/posts/PostCreate.js +++ b/examples/simple/src/posts/PostCreate.js @@ -1,5 +1,5 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; +import React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; import RichTextInput from 'ra-input-rich-text'; import { @@ -24,31 +24,20 @@ import { const saveWithNote = (values, basePath, redirectTo) => crudCreate('posts', { ...values, average_note: 10 }, basePath, redirectTo); -class SaveWithNoteButtonComponent extends Component { - handleClick = () => { - const { basePath, handleSubmit, redirect, saveWithNote } = this.props; +const SaveWithNoteButton = props => { + const dispatch = useDispatch(); + const { basePath, handleSubmit, redirect } = props; - return handleSubmit(values => { - saveWithNote(values, basePath, redirect); - }); - }; + const handleClick = useCallback( + () => + handleSubmit(values => { + dispatch(saveWithNote(values, basePath, redirect)); + }), + [basePath, redirect], + ); - render() { - const { handleSubmitWithRedirect, saveWithNote, ...props } = this.props; - - return ( - <SaveButton - handleSubmitWithRedirect={this.handleClick} - {...props} - /> - ); - } -} - -const SaveWithNoteButton = connect( - undefined, - { saveWithNote } -)(SaveWithNoteButtonComponent); + return <SaveButton {...props} handleSubmitWithRedirect={handleClick} />; +}; const PostCreateToolbar = props => ( <Toolbar {...props}> diff --git a/examples/simple/src/posts/PostList.js b/examples/simple/src/posts/PostList.js index 8ebfd9c012a..dfa70b14c2b 100644 --- a/examples/simple/src/posts/PostList.js +++ b/examples/simple/src/posts/PostList.js @@ -1,6 +1,6 @@ import BookIcon from '@material-ui/icons/Book'; import Chip from '@material-ui/core/Chip'; -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core/styles'; import React, { Children, Fragment, cloneElement } from 'react'; import { BooleanField, @@ -46,7 +46,7 @@ const PostFilter = props => ( </Filter> ); -const styles = theme => ({ +const useStyles = makeStyles(theme => ({ title: { maxWidth: '20em', overflow: 'hidden', @@ -59,7 +59,7 @@ const styles = theme => ({ }, }, publishedAt: { fontStyle: 'italic' }, -}); +})); const PostListBulkActions = props => ( <Fragment> @@ -68,16 +68,21 @@ const PostListBulkActions = props => ( </Fragment> ); -const PostListActionToolbar = withStyles({ +const usePostListActionToolbarStyles = makeStyles({ toolbar: { alignItems: 'center', display: 'flex', }, -})(({ classes, children, ...props }) => ( - <div className={classes.toolbar}> - {Children.map(children, button => cloneElement(button, props))} - </div> -)); +}); + +const PostListActionToolbar = ({ children, ...props }) => { + const classes = usePostListActionToolbarStyles(); + return ( + <div className={classes.toolbar}> + {Children.map(children, button => cloneElement(button, props))} + </div> + ); +}; const rowClick = (id, basePath, record) => { if (record.commentable) { @@ -91,58 +96,64 @@ const PostPanel = ({ id, record, resource }) => ( <div dangerouslySetInnerHTML={{ __html: record.body }} /> ); -const PostList = withStyles(styles)(({ classes, ...props }) => ( - <List - {...props} - bulkActionButtons={PostListBulkActions} - filters={PostFilter} - sort={{ field: 'published_at', order: 'DESC' }} - > - <Responsive - small={ - <SimpleList - primaryText={record => record.title} - secondaryText={record => `${record.views} views`} - tertiaryText={record => - new Date(record.published_at).toLocaleDateString() - } - /> - } - medium={ - <Datagrid rowClick={rowClick} expand={PostPanel}> - <TextField source="id" /> - <TextField source="title" cellClassName={classes.title} /> - <DateField - source="published_at" - cellClassName={classes.publishedAt} +const PostList = props => { + const classes = useStyles(); + return ( + <List + {...props} + bulkActionButtons={PostListBulkActions} + filters={PostFilter} + sort={{ field: 'published_at', order: 'DESC' }} + > + <Responsive + small={ + <SimpleList + primaryText={record => record.title} + secondaryText={record => `${record.views} views`} + tertiaryText={record => + new Date(record.published_at).toLocaleDateString() + } /> + } + medium={ + <Datagrid rowClick={rowClick} expand={PostPanel}> + <TextField source="id" /> + <TextField + source="title" + cellClassName={classes.title} + /> + <DateField + source="published_at" + cellClassName={classes.publishedAt} + /> - <BooleanField - source="commentable" - label="resources.posts.fields.commentable_short" - sortable={false} - /> - <NumberField source="views" /> - <ReferenceArrayField - label="Tags" - reference="tags" - source="tags" - sortBy="tags.name" - cellClassName={classes.hiddenOnSmallScreens} - headerClassName={classes.hiddenOnSmallScreens} - > - <SingleFieldList> - <ChipField source="name" /> - </SingleFieldList> - </ReferenceArrayField> - <PostListActionToolbar> - <EditButton /> - <ShowButton /> - </PostListActionToolbar> - </Datagrid> - } - /> - </List> -)); + <BooleanField + source="commentable" + label="resources.posts.fields.commentable_short" + sortable={false} + /> + <NumberField source="views" /> + <ReferenceArrayField + label="Tags" + reference="tags" + source="tags" + sortBy="tags.name" + cellClassName={classes.hiddenOnSmallScreens} + headerClassName={classes.hiddenOnSmallScreens} + > + <SingleFieldList> + <ChipField source="name" /> + </SingleFieldList> + </ReferenceArrayField> + <PostListActionToolbar> + <EditButton /> + <ShowButton /> + </PostListActionToolbar> + </Datagrid> + } + /> + </List> + ); +}; export default PostList; diff --git a/examples/simple/src/posts/ResetViewsButton.js b/examples/simple/src/posts/ResetViewsButton.js index 8e8fbbe1dfb..242139cb6a6 100644 --- a/examples/simple/src/posts/ResetViewsButton.js +++ b/examples/simple/src/posts/ResetViewsButton.js @@ -1,35 +1,41 @@ -import React, { Component } from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import { useDispatch } from 'react-redux'; import VisibilityOff from '@material-ui/icons/VisibilityOff'; import { startUndoable, crudUpdateMany, Button } from 'react-admin'; -class ResetViewsAction extends Component { - handleClick = () => { - const { basePath, startUndoable, resource, selectedIds } = this.props; - startUndoable( - crudUpdateMany(resource, selectedIds, { views: 0 }, basePath) - ); - }; +const ResetViewsButton = props => { + const dispatch = useDispatch(); + const { basePath, resource, selectedIds } = props; - render() { - return ( - <Button label="simple.action.resetViews" onClick={this.handleClick}> - <VisibilityOff /> - </Button> - ); - } -} + const handleClick = useCallback( + () => { + dispatch( + startUndoable( + crudUpdateMany( + resource, + selectedIds, + { views: 0 }, + basePath, + ), + ), + ); + }, + [basePath, resource, selectedIds], + ); -ResetViewsAction.propTypes = { + return ( + <Button label="simple.action.resetViews" onClick={handleClick}> + <VisibilityOff /> + </Button> + ); +}; + +ResetViewsButton.propTypes = { basePath: PropTypes.string, label: PropTypes.string, resource: PropTypes.string.isRequired, selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired, - startUndoable: PropTypes.func.isRequired, }; -export default connect( - undefined, - { startUndoable } -)(ResetViewsAction); +export default ResetViewsButton; diff --git a/examples/simple/src/tags/TagList.js b/examples/simple/src/tags/TagList.js index c81227682b9..fb8866a1810 100644 --- a/examples/simple/src/tags/TagList.js +++ b/examples/simple/src/tags/TagList.js @@ -5,13 +5,11 @@ import { List, SaveButton, ShowButton, - TextField, TextInput, } from 'react-admin'; import { DragPreview, IgnoreFormProps, - NodeView, NodeForm, Tree, NodeActions, diff --git a/examples/simple/src/users/UserEdit.js b/examples/simple/src/users/UserEdit.js index ef6d758c7a1..588c5e39dfa 100644 --- a/examples/simple/src/users/UserEdit.js +++ b/examples/simple/src/users/UserEdit.js @@ -13,29 +13,32 @@ import { Toolbar, required, } from 'react-admin'; -import { withStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import UserTitle from './UserTitle'; import Aside from './Aside'; -const toolbarStyles = { +const useToolbarStyles = makeStyles({ toolbar: { display: 'flex', justifyContent: 'space-between', }, -}; +}); /** * Custom Toolbar for the Edit form * * Save with undo, but delete with confirm */ -const UserEditToolbar = withStyles(toolbarStyles)(props => ( - <Toolbar {...props}> - <SaveButton /> - <DeleteWithConfirmButton /> - </Toolbar> -)); +const UserEditToolbar = props => { + const classes = useToolbarStyles(); + return ( + <Toolbar {...props} classes={classes}> + <SaveButton /> + <DeleteWithConfirmButton /> + </Toolbar> + ); +}; const UserEdit = ({ permissions, ...props }) => ( <Edit title={UserTitle} aside={Aside} {...props}>