diff --git a/packages/ra-ui-materialui/src/list/SimpleList.spec.tsx b/packages/ra-ui-materialui/src/list/SimpleList.spec.tsx index 7181d94aed0..ae5350f12dd 100644 --- a/packages/ra-ui-materialui/src/list/SimpleList.spec.tsx +++ b/packages/ra-ui-materialui/src/list/SimpleList.spec.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { render, waitFor, within } from '@testing-library/react'; +import { fireEvent, render, waitFor, within } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router-dom'; import { ListContext } from 'ra-core'; @@ -8,7 +8,7 @@ import SimpleList from './SimpleList'; import TextField from '../field/TextField'; const renderWithRouter = children => { - const history = createMemoryHistory(); + const history = createMemoryHistory({ initialEntries: ['/posts'] }); return { history, @@ -60,7 +60,7 @@ describe('', () => { (record, id, basePath) => `${basePath}/${id}/details`, ], ])('should support %s linkType', async (_, expectedUrl, linkType) => { - const { getByText } = renderWithRouter( + const { history, getByText } = renderWithRouter( ', () => { ); + fireEvent.click(getByText('1')); await waitFor(() => { - expect(getByText('1').closest('a').getAttribute('href')).toEqual( + expect(history.entries[history.length - 1].pathname).toEqual( expectedUrl ); }); }); + it('should not render a link if linkType is false', async () => { + const { getByText } = renderWithRouter( + + record.id.toString()} + secondaryText={} + /> + + ); + + await waitFor(() => { + expect(getByText('1').closest('a')).toBeNull(); + }); + }); }); diff --git a/packages/ra-ui-materialui/src/list/SimpleList.tsx b/packages/ra-ui-materialui/src/list/SimpleList.tsx index 9bc4d526b12..ef34b2a7aa3 100644 --- a/packages/ra-ui-materialui/src/list/SimpleList.tsx +++ b/packages/ra-ui-materialui/src/list/SimpleList.tsx @@ -1,11 +1,5 @@ import * as React from 'react'; -import { - isValidElement, - ReactNode, - ReactElement, - useEffect, - useState, -} from 'react'; +import { isValidElement, ReactNode, ReactElement, useCallback } from 'react'; import PropTypes from 'prop-types'; import { Avatar, @@ -18,7 +12,7 @@ import { ListItemText, } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; -import { Link } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { Identifier, linkToRecord, @@ -140,7 +134,7 @@ const SimpleList = ( resource={resource} > { resource, } = props; const classes = useLinkOrNotStyles({ classes: classesOverride }); - const [effect, setEffect] = useState(() => - linkType === 'edit' || linkType === true - ? linkToRecord(basePath || `/${resource}`, id) - : linkToRecord(basePath || `/${resource}`, id, 'show') - ); + const history = useHistory(); - useEffect(() => { - if (typeof linkType !== 'function') { - return; - } - const getEffect = async () => { - if (typeof linkType === 'function') { - setEffect( - await linkType(record, id, basePath || `/${resource}`) - ); - } - }; + const handleClick = useCallback( + async event => { + if (!linkType) return; + event.persist(); - getEffect(); - }, [basePath, id, linkType, record, resource]); + const effect = + typeof linkType === 'function' + ? await linkType(record, id, basePath || `/${resource}`) + : linkType; + switch (effect) { + case true: + case 'edit': + history.push(linkToRecord(basePath || `/${resource}`, id)); + return; + case 'show': + history.push( + linkToRecord(basePath || `/${resource}`, id, 'show') + ); + return; + default: + if (effect) history.push(effect); + return; + } + }, + [basePath, history, id, record, resource, linkType] + ); - return typeof effect === 'string' ? ( - + return linkType !== false ? ( + ) : ( {children} ); }; export type FunctionLinkType = ( - record: Record, + recordOrId: Record, id: Identifier, basePath?: string ) => string | Promise;