Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions docs/SimpleList.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const PostList = () => (
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType={record => record.canEdit ? "edit" : "show"}
rowClick={(id, resource, record) => record.canEdit ? "edit" : "show"}
rowSx={record => ({ backgroundColor: record.nb_views >= 500 ? '#efe' : 'white' })}
/>
</List>
Expand All @@ -44,7 +44,7 @@ export const PostList = () => (
| `primaryText` | Optional | mixed | record representation | The primary text to display. |
| `secondaryText` | Optional | mixed | | The secondary text to display. |
| `tertiaryText` | Optional | mixed | | The tertiary text to display. |
| `linkType` | Optional |mixed | `"edit"` | The target of each item click. |
| `rowClick` | Optional |mixed | `"edit"` | The action to trigger when the user clicks on a row. |
| `leftAvatar` | Optional | function | | A function returning an `<Avatar>` component to display before the primary text. |
| `leftIcon` | Optional | function | | A function returning an `<Icon>` component to display before the primary text. |
| `rightAvatar` | Optional | function | | A function returning an `<Avatar>` component to display after the primary text. |
Expand Down Expand Up @@ -80,9 +80,9 @@ This prop should be a function returning an `<Avatar>` component. When present,

This prop should be a function returning an `<Icon>` component. When present, the `<ListItem>` renders a `<ListIcon>` before the `<ListItemText>`

## `linkType`
## `rowClick`

The `<SimpleList>` items link to the edition page by default. You can also set the `linkType` prop to `show` directly to link to the `<Show>` page instead.
The `<SimpleList>` items link to the edition page by default. You can also set the `rowClick` prop to `show` directly to link to the `<Show>` page instead.

```jsx
import { List, SimpleList } from 'react-admin';
Expand All @@ -93,17 +93,18 @@ export const PostList = () => (
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType="show"
rowClick="show"
/>
</List>
);
```

`linkType` accepts the following values:
`rowClick` accepts the following values:

* `linkType="edit"`: links to the edit page. This is the default behavior.
* `linkType="show"`: links to the show page.
* `linkType={false}`: does not create any link.
* `rowClick="edit"`: links to the edit page. This is the default behavior.
* `rowClick="show"`: links to the show page.
* `rowClick={false}`: does not link to anything.
* `rowClick={(id, resource, record) => path}`: path can be any of the above values

## `primaryText`

Expand Down Expand Up @@ -254,7 +255,7 @@ export const PostList = () => {
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType={record => record.canEdit ? "edit" : "show"}
rowClick={(id, resource, record) => record.canEdit ? "edit" : "show"}
/>
) : (
<Datagrid>
Expand Down
5 changes: 5 additions & 0 deletions packages/ra-core/src/routing/useGetPathForRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export const useGetPathForRecord = <RecordType extends RaRecord = RaRecord>(
useEffect(() => {
if (!record) return;

if (link === false) {
setPath(false);
return;
}

// Handle the inferred link type case
if (link == null) {
// We must check whether the resource has an edit view because if there is no
Expand Down
139 changes: 87 additions & 52 deletions packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,41 @@ import {
waitFor,
within,
} from '@testing-library/react';
import { ListContext, ResourceContextProvider } from 'ra-core';
import {
ListContext,
ResourceContextProvider,
ResourceDefinitionContextProvider,
} from 'ra-core';
import { Location } from 'react-router';

import { AdminContext } from '../../AdminContext';
import { SimpleList } from './SimpleList';
import { TextField } from '../../field/TextField';
import {
LinkType,
NoPrimaryText,
RowClick,
Standalone,
StandaloneEmpty,
} from './SimpleList.stories';
import { Basic } from '../filter/FilterButton.stories';

const Wrapper = ({ children }: any) => (
<AdminContext>
<ResourceContextProvider value="posts">
{children}
</ResourceContextProvider>
<ResourceDefinitionContextProvider
definitions={{
posts: {
name: 'posts',
hasList: true,
hasEdit: true,
hasShow: true,
},
}}
>
<ResourceContextProvider value="posts">
{children}
</ResourceContextProvider>
</ResourceDefinitionContextProvider>
</AdminContext>
);

Expand Down Expand Up @@ -59,58 +77,24 @@ describe('<SimpleList />', () => {
});

it.each([
[
'edit',
'edit',
['http://localhost/#/posts/1', 'http://localhost/#/posts/2'],
],
[
'show',
'show',
[
'http://localhost/#/posts/1/show',
'http://localhost/#/posts/2/show',
],
],
[
'custom',
(record, id) => `/posts/${id}/custom`,
[
'http://localhost/#/posts/1/custom',
'http://localhost/#/posts/2/custom',
],
],
['edit', 'edit', '/books/1'],
['show', 'show', '/books/1/show'],
['custom', (record, id) => `/books/${id}/custom`, '/books/1/custom'],
])(
'should render %s links for each item',
async (_, link, expectedUrls) => {
'should render %s links for each item with linkType',
async (_, linkType, expectedUrls) => {
let location: Location;
render(
<ListContext.Provider
value={{
isLoading: false,
data: [
{ id: 1, title: 'foo' },
{ id: 2, title: 'bar' },
],
total: 2,
resource: 'posts',
<LinkType
linkType={linkType}
locationCallback={l => {
location = l;
}}
>
<SimpleList
linkType={link}
primaryText={record => record.id.toString()}
secondaryText={<TextField source="title" />}
/>
</ListContext.Provider>,
{ wrapper: Wrapper }
/>
);

fireEvent.click(await screen.findByText('War and Peace'));
await waitFor(() => {
expect(screen.getByText('1').closest('a').href).toEqual(
expectedUrls[0]
);
expect(screen.getByText('2').closest('a').href).toEqual(
expectedUrls[1]
);
expect(location?.pathname).toEqual(expectedUrls);
});
}
);
Expand Down Expand Up @@ -143,6 +127,57 @@ describe('<SimpleList />', () => {
});
});

it.each([
['edit', 'edit', '/books/1'],
['show', 'show', '/books/1/show'],
['custom', id => `/books/${id}/custom`, '/books/1/custom'],
])(
'should render %s links for each item with rowClick',
async (_, rowClick, expectedUrls) => {
let location: Location;
render(
<RowClick
rowClick={rowClick}
locationCallback={l => {
location = l;
}}
/>
);
fireEvent.click(await screen.findByText('War and Peace'));
await waitFor(() => {
expect(location?.pathname).toEqual(expectedUrls);
});
}
);

it('should not render links if rowClick is false', async () => {
render(
<ListContext.Provider
value={{
isLoading: false,
data: [
{ id: 1, title: 'foo' },
{ id: 2, title: 'bar' },
],
total: 2,
resource: 'posts',
}}
>
<SimpleList
rowClick={false}
primaryText={record => record.id.toString()}
secondaryText={<TextField source="title" />}
/>
</ListContext.Provider>,
{ wrapper: Wrapper }
);

await waitFor(() => {
expect(screen.getByText('1').closest('a')).toBeNull();
expect(screen.getByText('2').closest('a')).toBeNull();
});
});

it('should display a message when there is no result', () => {
render(
<ListContext.Provider
Expand Down Expand Up @@ -205,7 +240,7 @@ describe('<SimpleList />', () => {
});
it('should display a message when there is no result', async () => {
render(<StandaloneEmpty />);
await screen.findByText('No results found.');
await screen.findByText('ra.navigation.no_results');
});
});
});
Loading