Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 11771af

Browse files
committedNov 10, 2023
Add <SingleFieldList empty> prop to customize empty value
Closes #7634
1 parent d347bf0 commit 11771af

12 files changed

+384
-124
lines changed
 

‎docs/SingleFieldList.md

+50-15
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,30 @@ Use `<SingleFieldList>` when you want to display only one property for each reco
1313

1414
## Usage
1515

16-
Use `<SingleFieldList>` wherever there is a `ListContext`. It is especially useful as child of `<ReferenceManyField>` and `<ReferenceArrayField>` components. `<SingleFieldList>` expects a single `<Field>` as child.
16+
`<SingleFieldList>` grabs the current `ListContext`, and renders a Material UI `<Stack>` with one `<ChipField>` for each record in the list, using the `recordRepresentation`. It is especially useful as child of `<ReferenceManyField>` and `<ReferenceArrayField>` components.
1717

1818
```jsx
19-
<SingleFieldList>
20-
<ChipField source="name" />
21-
</SingleFieldList>
19+
import {
20+
Show,
21+
SimpleShowLayout,
22+
TextField,
23+
ReferenceArrayField,
24+
SingleFieldList
25+
} from 'react-admin';
26+
27+
const PostShow = () => (
28+
<Show>
29+
<SimpleShowLayout>
30+
<TextField source="title" />
31+
<ReferenceArrayField label="Tags" reference="tags" source="tags">
32+
<SingleFieldList />
33+
</ReferenceArrayField>
34+
</SimpleShowLayout>
35+
</Show>
36+
);
2237
```
2338

24-
The following example shows how to use `<SingleFieldList>` to display a list of tags for each post in a Datagrid:
39+
You can customize how each record is displayed by passing a Field component as child. For example, here is how to use `<SingleFieldList>` to display a list of tags for each post in a Datagrid:
2540

2641
```jsx
2742
import {
@@ -45,9 +60,7 @@ const PostList = () => (
4560
<BooleanField source="commentable" />
4661
<NumberField source="views" />
4762
<ReferenceArrayField label="Tags" reference="tags" source="tags">
48-
<SingleFieldList>
49-
<ChipField source="name" />
50-
</SingleFieldList>
63+
<SingleFieldList />
5164
</ReferenceArrayField>
5265
</Datagrid>
5366
</List>
@@ -60,10 +73,34 @@ const PostList = () => (
6073

6174
`<SingleFieldList>` accepts the following props:
6275

63-
| Prop | Required | Type | Default | Description |
64-
| ----------- | -------- | ------------------------- | ------- | --------------------------------------------- |
65-
| `linkType` | Optional | `'edit' | 'show' | false` | `edit` | The target of the link on each item |
66-
| `sx` | Optional | `object` | | The sx props of the Material UI Box component |
76+
| Prop | Required | Type | Default | Description |
77+
| ----------- | -------- | ------------------------- | ------- | ----------------------------------------------- |
78+
| `children` | Optional | `ReactNode` | | React element to render for each record |
79+
| `empty` | Optional | `ReactNode` | | React element to display when the list is empty |
80+
| `linkType` | Optional | `'edit' | 'show' | false` | `edit` | The target of the link on each item |
81+
| `sx` | Optional | `object` | | The sx props of the Material UI Box component |
82+
83+
Additional props are passed down to the underlying [Material UI `<Stack>` component](https://mui.com/material-ui/react-stack/).
84+
85+
## `children`
86+
87+
By default, `<SingleFieldList>` renders a `<ChipField>` for each record. You can customize the rendering by passing a Field component as child.
88+
89+
For example, if you want to customize the field name used by the `<ChipField>`:
90+
91+
```jsx
92+
<SingleFieldList>
93+
<ChipField source="tag" clickable />
94+
</SingleFieldList>
95+
```
96+
97+
## `empty`
98+
99+
When the list is empty, `<SingleFieldList>` displays nothing. You can customize this behavior by passing a React element as the `empty` prop. For example, to display a message:
100+
101+
```jsx
102+
<SingleFieldList empty={<p>Nothing to display</p>} />
103+
```
67104

68105
## `linkType`
69106

@@ -76,9 +113,7 @@ The `<SingleFieldList>` items link to the edition page by default. You can set t
76113
reference="tags"
77114
source="tags"
78115
>
79-
<SingleFieldList linkType="show">
80-
<ChipField source="name" />
81-
</SingleFieldList>
116+
<SingleFieldList linkType="show" />
82117
</ReferenceArrayField>
83118
```
84119

‎examples/crm/src/contacts/ContactList.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ const ContactListContent = () => {
8888
<TextField source="name" />
8989
</ReferenceField>{' '}
9090
{contact.nb_notes &&
91-
`- ${contact.nb_notes} notes `}
91+
`- ${contact.nb_notes} notes`}
92+
&nbsp;&nbsp;
9293
<TagsList />
9394
</>
9495
}

‎examples/crm/src/contacts/TagsList.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const TagsList = () => (
2626
source="tags"
2727
reference="tags"
2828
>
29-
<SingleFieldList linkType={false} component="span">
29+
<SingleFieldList linkType={false}>
3030
<ColoredChipField source="name" variant="outlined" size="small" />
3131
</SingleFieldList>
3232
</ReferenceArrayField>

‎examples/simple/src/posts/PostList.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ const PostListDesktop = () => (
192192
cellClassName="hiddenOnSmallScreens"
193193
headerClassName="hiddenOnSmallScreens"
194194
>
195-
<SingleFieldList sx={{ my: -2 }}>
196-
<ChipField source="name.en" size="small" />
195+
<SingleFieldList>
196+
<ChipField clickable source="name.en" size="small" />
197197
</SingleFieldList>
198198
</ReferenceArrayField>
199199
<NumberField source="average_note" />

‎examples/simple/src/posts/PostShow.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const PostShow = () => {
7575
<ChipField
7676
source={`name.${locale}`}
7777
size="small"
78+
clickable
7879
/>
7980
</SingleFieldList>
8081
</ReferenceArrayField>

‎packages/ra-ui-materialui/src/field/ChipField.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,5 @@ const StyledChip = styled(Chip, {
7171
name: PREFIX,
7272
overridesResolver: (props, styles) => styles.root,
7373
})({
74-
[`&.${ChipFieldClasses.chip}`]: { margin: 4, cursor: 'inherit' },
74+
[`&.${ChipFieldClasses.chip}`]: { cursor: 'inherit' },
7575
});

‎packages/ra-ui-materialui/src/field/ReferenceArrayField.stories.tsx

+79-25
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,102 @@
11
import * as React from 'react';
22
import fakeRestProvider from 'ra-data-fakerest';
3+
import { CardContent } from '@mui/material';
4+
import { ResourceDefinitionContextProvider } from 'ra-core';
35

46
import { AdminContext } from '../AdminContext';
57
import { Datagrid } from '../list';
68
import { ReferenceArrayField } from './ReferenceArrayField';
79
import { TextField } from './TextField';
8-
import { Show } from '../detail';
9-
import { CardContent } from '@mui/material';
10+
import { Show, SimpleShowLayout } from '../detail';
11+
12+
export default { title: 'ra-ui-materialui/fields/ReferenceArrayField' };
1013

1114
const fakeData = {
12-
bands: [{ id: 1, name: 'band_1', members: [1, '2', '3'] }],
15+
bands: [{ id: 1, name: 'The Beatles', members: [1, 2, 3, 4] }],
1316
artists: [
14-
{ id: 1, name: 'artist_1' },
15-
{ id: 2, name: 'artist_2' },
16-
{ id: 3, name: 'artist_3' },
17-
{ id: 4, name: 'artist_4' },
17+
{ id: 1, name: 'John Lennon' },
18+
{ id: 2, name: 'Paul McCartney' },
19+
{ id: 3, name: 'Ringo Star' },
20+
{ id: 4, name: 'George Harrison' },
21+
{ id: 5, name: 'Mick Jagger' },
1822
],
1923
};
20-
21-
export default { title: 'ra-ui-materialui/fields/ReferenceArrayField' };
22-
2324
const dataProvider = fakeRestProvider(fakeData, false);
2425

25-
export const DifferentIdTypes = () => {
26-
return (
27-
<AdminContext dataProvider={dataProvider}>
28-
<CardContent>
29-
<Show resource="bands" id={1} sx={{ width: 600 }}>
30-
<TextField source="name" fullWidth />
31-
<ReferenceArrayField
32-
fullWidth
33-
source="members"
34-
reference="artists"
35-
>
26+
const resouceDefs = {
27+
artists: {
28+
name: 'artists',
29+
hasList: true,
30+
hasEdit: true,
31+
hasShow: true,
32+
hasCreate: true,
33+
recordRepresentation: 'name',
34+
},
35+
};
36+
export const Basic = () => (
37+
<AdminContext dataProvider={dataProvider}>
38+
<ResourceDefinitionContextProvider definitions={resouceDefs}>
39+
<Show resource="bands" id={1} sx={{ width: 600 }}>
40+
<SimpleShowLayout>
41+
<TextField source="name" />
42+
<ReferenceArrayField source="members" reference="artists" />
43+
</SimpleShowLayout>
44+
</Show>
45+
</ResourceDefinitionContextProvider>
46+
</AdminContext>
47+
);
48+
49+
export const Children = () => (
50+
<AdminContext dataProvider={dataProvider}>
51+
<ResourceDefinitionContextProvider definitions={resouceDefs}>
52+
<Show resource="bands" id={1} sx={{ width: 600 }}>
53+
<SimpleShowLayout>
54+
<TextField source="name" />
55+
<ReferenceArrayField source="members" reference="artists">
3656
<Datagrid bulkActionButtons={false}>
3757
<TextField source="id" />
3858
<TextField source="name" />
3959
</Datagrid>
4060
</ReferenceArrayField>
41-
</Show>
42-
</CardContent>
43-
</AdminContext>
44-
);
61+
</SimpleShowLayout>
62+
</Show>
63+
</ResourceDefinitionContextProvider>
64+
</AdminContext>
65+
);
66+
67+
const fakeDataWidthDifferentIdTypes = {
68+
bands: [{ id: 1, name: 'band_1', members: [1, '2', '3'] }],
69+
artists: [
70+
{ id: 1, name: 'artist_1' },
71+
{ id: 2, name: 'artist_2' },
72+
{ id: 3, name: 'artist_3' },
73+
{ id: 4, name: 'artist_4' },
74+
],
4575
};
76+
const dataProviderWithDifferentIdTypes = fakeRestProvider(
77+
fakeDataWidthDifferentIdTypes,
78+
false
79+
);
80+
81+
export const DifferentIdTypes = () => (
82+
<AdminContext dataProvider={dataProviderWithDifferentIdTypes}>
83+
<CardContent>
84+
<Show resource="bands" id={1} sx={{ width: 600 }}>
85+
<TextField source="name" fullWidth />
86+
<ReferenceArrayField
87+
fullWidth
88+
source="members"
89+
reference="artists"
90+
>
91+
<Datagrid bulkActionButtons={false}>
92+
<TextField source="id" />
93+
<TextField source="name" />
94+
</Datagrid>
95+
</ReferenceArrayField>
96+
</Show>
97+
</CardContent>
98+
</AdminContext>
99+
);
46100

47101
const dataProviderWithLog = {
48102
...dataProvider,

‎packages/ra-ui-materialui/src/field/ReferenceArrayField.tsx

+3-23
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,15 @@ import {
1010
FilterPayload,
1111
ResourceContextProvider,
1212
useRecordContext,
13-
useResourceDefinition,
1413
RaRecord,
1514
} from 'ra-core';
1615
import { styled } from '@mui/material/styles';
1716
import { SxProps } from '@mui/system';
17+
import { UseQueryOptions } from 'react-query';
1818

1919
import { fieldPropTypes, FieldProps } from './types';
2020
import { LinearProgress } from '../layout';
2121
import { SingleFieldList } from '../list/SingleFieldList';
22-
import { ChipField } from './ChipField';
23-
import { UseQueryOptions } from 'react-query';
2422

2523
/**
2624
* A container component that fetches records from another resource specified
@@ -152,27 +150,9 @@ export interface ReferenceArrayFieldViewProps
152150
Omit<ListControllerProps, 'queryOptions'> {}
153151

154152
export const ReferenceArrayFieldView: FC<ReferenceArrayFieldViewProps> = props => {
155-
const { children, pagination, reference, className, sx } = props;
153+
const { children, pagination, className, sx } = props;
156154
const { isLoading, total } = useListContext(props);
157155

158-
const { recordRepresentation } = useResourceDefinition({
159-
resource: reference,
160-
});
161-
let child = children ? (
162-
children
163-
) : (
164-
<SingleFieldList>
165-
<ChipField
166-
source={
167-
typeof recordRepresentation === 'string'
168-
? recordRepresentation
169-
: 'id'
170-
}
171-
size="small"
172-
/>
173-
</SingleFieldList>
174-
);
175-
176156
return (
177157
<Root className={className} sx={sx}>
178158
{isLoading ? (
@@ -181,7 +161,7 @@ export const ReferenceArrayFieldView: FC<ReferenceArrayFieldViewProps> = props =
181161
/>
182162
) : (
183163
<span>
184-
{child}
164+
{children || <SingleFieldList />}
185165
{pagination && total !== undefined ? pagination : null}
186166
</span>
187167
)}

‎packages/ra-ui-materialui/src/list/SimpleList/SimpleList.tsx

-5
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,6 @@ export const SimpleList = <RecordType extends RaRecord = any>(
103103
);
104104
}
105105

106-
/**
107-
* Once loaded, the data for the list may be empty. Instead of
108-
* displaying the table header with zero data rows,
109-
* the SimpleList the empty component.
110-
*/
111106
if (data == null || data.length === 0 || total === 0) {
112107
if (empty) {
113108
return empty;

‎packages/ra-ui-materialui/src/list/SingleFieldList.spec.tsx

+38-22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ListContext, ResourceContextProvider } from 'ra-core';
55
import { AdminContext } from '../AdminContext';
66
import { SingleFieldList } from './SingleFieldList';
77
import { ChipField } from '../field';
8+
import { Empty } from './SingleFieldList.stories';
89

910
describe('<SingleFieldList />', () => {
1011
it('should render a link to the Edit page of the related record by default', () => {
@@ -149,28 +150,43 @@ describe('<SingleFieldList />', () => {
149150
});
150151
});
151152

152-
it('should render no link when the linkType is false', () => {
153-
render(
154-
<AdminContext>
155-
<ListContext.Provider
156-
value={{
157-
data: [
158-
{ id: 1, title: 'foo' },
159-
{ id: 2, title: 'bar' },
160-
],
161-
resource: 'bar',
162-
}}
163-
>
164-
<SingleFieldList linkType={false}>
165-
<ChipField source="title" />
166-
</SingleFieldList>
167-
</ListContext.Provider>
168-
</AdminContext>
169-
);
153+
describe('linkType', () => {
154+
it('should render no link when the linkType is false', () => {
155+
render(
156+
<AdminContext>
157+
<ListContext.Provider
158+
value={{
159+
data: [
160+
{ id: 1, title: 'foo' },
161+
{ id: 2, title: 'bar' },
162+
],
163+
resource: 'bar',
164+
}}
165+
>
166+
<SingleFieldList linkType={false}>
167+
<ChipField source="title" />
168+
</SingleFieldList>
169+
</ListContext.Provider>
170+
</AdminContext>
171+
);
170172

171-
const linkElements = screen.queryAllByRole('link');
172-
expect(linkElements).toHaveLength(0);
173-
expect(screen.queryByText('foo')).not.toBeNull();
174-
expect(screen.queryByText('bar')).not.toBeNull();
173+
const linkElements = screen.queryAllByRole('link');
174+
expect(linkElements).toHaveLength(0);
175+
expect(screen.queryByText('foo')).not.toBeNull();
176+
expect(screen.queryByText('bar')).not.toBeNull();
177+
});
178+
});
179+
180+
describe('empty', () => {
181+
it('should use the empty element when there is no data', () => {
182+
render(<Empty />);
183+
expect(screen.queryByText('No genres')).not.toBeNull();
184+
});
185+
it('should not render the empty element while loading', () => {
186+
render(
187+
<Empty listContext={{ isLoading: true, data: [] } as any} />
188+
);
189+
expect(screen.queryByText('No genres')).toBeNull();
190+
});
175191
});
176192
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import * as React from 'react';
2+
import {
3+
ListContextProvider,
4+
ResourceContextProvider,
5+
ResourceDefinitionContextProvider,
6+
useList,
7+
} from 'ra-core';
8+
import { MemoryRouter } from 'react-router-dom';
9+
import { Typography, Divider as MuiDivider } from '@mui/material';
10+
11+
import { SingleFieldList } from './SingleFieldList';
12+
import { ChipField } from '../field/ChipField';
13+
import { TextField } from '../field/TextField';
14+
15+
const bookGenres = [
16+
{ id: 0, name: 'Fiction' },
17+
{ id: 1, name: 'Science-fiction' },
18+
{ id: 2, name: 'Horror' },
19+
{ id: 3, name: 'Biography' },
20+
{ id: 4, name: 'Historical' },
21+
{ id: 5, name: 'Crime' },
22+
{ id: 6, name: 'Romance' },
23+
{ id: 7, name: 'Humor' },
24+
];
25+
26+
export default {
27+
title: 'ra-ui-materialui/list/SingleFieldList',
28+
};
29+
30+
const Wrapper = ({
31+
children,
32+
data = [bookGenres[2], bookGenres[4], bookGenres[1]],
33+
}) => {
34+
const listContextValue = useList({
35+
data,
36+
});
37+
return (
38+
<MemoryRouter>
39+
<ResourceDefinitionContextProvider
40+
definitions={{
41+
books: {
42+
name: 'books',
43+
hasList: true,
44+
hasEdit: true,
45+
hasShow: true,
46+
hasCreate: true,
47+
recordRepresentation: 'name',
48+
},
49+
}}
50+
>
51+
<ResourceContextProvider value="books">
52+
<ListContextProvider value={listContextValue}>
53+
{children}
54+
</ListContextProvider>
55+
</ResourceContextProvider>
56+
</ResourceDefinitionContextProvider>
57+
</MemoryRouter>
58+
);
59+
};
60+
const Title = ({ children }) => (
61+
<Typography ml={1} mt={2} mb={1}>
62+
{children}
63+
</Typography>
64+
);
65+
66+
export const Basic = () => (
67+
<Wrapper>
68+
<SingleFieldList />
69+
</Wrapper>
70+
);
71+
72+
export const Children = () => (
73+
<Wrapper>
74+
<Title>Text Field</Title>
75+
<SingleFieldList>
76+
<TextField
77+
source="name"
78+
sx={{
79+
m: 1,
80+
p: 0.5,
81+
border: '1px solid grey',
82+
borderRadius: 2,
83+
}}
84+
/>
85+
</SingleFieldList>
86+
<Title>Chip Field</Title>
87+
<SingleFieldList>
88+
<ChipField source="name" />
89+
</SingleFieldList>
90+
<Title>Chip Field small</Title>
91+
<SingleFieldList>
92+
<ChipField source="name" size="small" />
93+
</SingleFieldList>
94+
</Wrapper>
95+
);
96+
97+
export const LinkType = () => (
98+
<Wrapper>
99+
<Title>Default (Edit link)</Title>
100+
<SingleFieldList />
101+
<Title>Show link</Title>
102+
<SingleFieldList linkType="show" />
103+
<Title>No link</Title>
104+
<SingleFieldList linkType={false} />
105+
</Wrapper>
106+
);
107+
108+
export const NoData = () => (
109+
<Wrapper data={[]}>
110+
<SingleFieldList />
111+
</Wrapper>
112+
);
113+
114+
export const Empty = ({ listContext = { data: [] } }) => (
115+
<ListContextProvider value={listContext as any}>
116+
<SingleFieldList empty={<div>No genres</div>} />
117+
</ListContextProvider>
118+
);
119+
120+
export const Loading = () => (
121+
<ListContextProvider value={{ isLoading: true } as any}>
122+
<SingleFieldList />
123+
</ListContextProvider>
124+
);
125+
126+
export const Direction = () => (
127+
<Wrapper>
128+
<Title>Default (row)</Title>
129+
<SingleFieldList />
130+
<Title>Column</Title>
131+
<SingleFieldList direction="column" />
132+
</Wrapper>
133+
);
134+
135+
export const Gap = () => (
136+
<Wrapper>
137+
<Title>No gap</Title>
138+
<SingleFieldList gap={0} />
139+
<Title>Default (1)</Title>
140+
<SingleFieldList />
141+
<Title>Custom gap</Title>
142+
<SingleFieldList gap={2} />
143+
</Wrapper>
144+
);
145+
146+
export const Divider = () => (
147+
<Wrapper>
148+
<SingleFieldList
149+
divider={<MuiDivider orientation="vertical" flexItem />}
150+
/>
151+
</Wrapper>
152+
);
153+
154+
export const SX = () => (
155+
<Wrapper>
156+
<SingleFieldList sx={{ border: '1px solid grey' }} />
157+
</Wrapper>
158+
);
159+
160+
export const Controlled = () => (
161+
<Wrapper>
162+
<SingleFieldList
163+
data={[bookGenres[3], bookGenres[6], bookGenres[7], bookGenres[2]]}
164+
resource="book_genres"
165+
/>
166+
</Wrapper>
167+
);

‎packages/ra-ui-materialui/src/list/SingleFieldList.tsx

+40-29
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
import * as React from 'react';
2-
import { LinearProgress, styled, SxProps } from '@mui/material';
3-
import {
4-
cloneElement,
5-
Children,
6-
HtmlHTMLAttributes,
7-
ComponentType,
8-
} from 'react';
2+
import { Chip, Stack, StackProps, styled } from '@mui/material';
3+
import { cloneElement, Children, ComponentType } from 'react';
94
import PropTypes from 'prop-types';
105
import {
116
sanitizeListRestProps,
127
useListContext,
138
useResourceContext,
149
RaRecord,
1510
RecordContextProvider,
11+
RecordRepresentation,
1612
ComponentPropType,
1713
useCreatePath,
1814
} from 'ra-core';
1915

16+
import { LinearProgress } from '../layout/LinearProgress';
2017
import { Link } from '../Link';
2118

2219
/**
@@ -55,20 +52,36 @@ export const SingleFieldList = (props: SingleFieldListProps) => {
5552
const {
5653
className,
5754
children,
55+
empty,
5856
linkType = 'edit',
5957
component: Component = Root,
58+
gap = 1,
59+
direction = 'row',
6060
...rest
6161
} = props;
62-
const { data, isLoading } = useListContext(props);
62+
const { data, total, isLoading } = useListContext(props);
6363
const resource = useResourceContext(props);
6464
const createPath = useCreatePath();
6565

6666
if (isLoading === true) {
6767
return <LinearProgress />;
6868
}
6969

70+
if (data == null || data.length === 0 || total === 0) {
71+
if (empty) {
72+
return empty;
73+
}
74+
75+
return null;
76+
}
77+
7078
return (
71-
<Component className={className} {...sanitizeListRestProps(rest)}>
79+
<Component
80+
gap={gap}
81+
direction={direction}
82+
className={className}
83+
{...sanitizeListRestProps(rest)}
84+
>
7285
{data.map((record, rowIndex) => {
7386
const resourceLinkPath = !linkType
7487
? false
@@ -89,12 +102,9 @@ export const SingleFieldList = (props: SingleFieldListProps) => {
89102
to={resourceLinkPath}
90103
onClick={stopPropagation}
91104
>
92-
{cloneElement(Children.only(children), {
93-
record,
94-
resource,
95-
// Workaround to force ChipField to be clickable
96-
onClick: handleClick,
97-
})}
105+
{children || (
106+
<DefaultChildComponent clickable />
107+
)}
98108
</Link>
99109
</RecordContextProvider>
100110
);
@@ -105,7 +115,7 @@ export const SingleFieldList = (props: SingleFieldListProps) => {
105115
value={record}
106116
key={record.id ?? `row${rowIndex}`}
107117
>
108-
{children}
118+
{children || <DefaultChildComponent />}
109119
</RecordContextProvider>
110120
);
111121
})}
@@ -114,12 +124,12 @@ export const SingleFieldList = (props: SingleFieldListProps) => {
114124
};
115125

116126
SingleFieldList.propTypes = {
117-
children: PropTypes.element.isRequired,
127+
children: PropTypes.element,
118128
classes: PropTypes.object,
119129
className: PropTypes.string,
120130
component: ComponentPropType,
121131
data: PropTypes.any,
122-
ids: PropTypes.array,
132+
empty: PropTypes.element,
123133
// @ts-ignore
124134
linkType: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
125135
resource: PropTypes.string,
@@ -137,17 +147,16 @@ SingleFieldList.propTypes = {
137147
};
138148

139149
export interface SingleFieldListProps<RecordType extends RaRecord = any>
140-
extends HtmlHTMLAttributes<HTMLDivElement> {
150+
extends StackProps {
141151
className?: string;
142-
143152
component?: string | ComponentType<any>;
153+
empty?: React.ReactElement;
144154
linkType?: string | false;
145-
children: React.ReactElement;
155+
children?: React.ReactElement;
146156
// can be injected when using the component without context
147157
data?: RecordType[];
148158
total?: number;
149159
loaded?: boolean;
150-
sx?: SxProps;
151160
}
152161

153162
const PREFIX = 'RaSingleFieldList';
@@ -156,13 +165,11 @@ export const SingleFieldListClasses = {
156165
link: `${PREFIX}-link`,
157166
};
158167

159-
const Root = styled('div', {
168+
const Root = styled(Stack, {
160169
name: PREFIX,
161170
overridesResolver: (props, styles) => styles.root,
162171
})(({ theme }) => ({
163-
display: 'flex',
164172
flexWrap: 'wrap',
165-
166173
[`& .${SingleFieldListClasses.link}`]: {
167174
textDecoration: 'none',
168175
'& > *': {
@@ -174,7 +181,11 @@ const Root = styled('div', {
174181
// useful to prevent click bubbling in a datagrid with rowClick
175182
const stopPropagation = e => e.stopPropagation();
176183

177-
// Our handleClick does nothing as we wrap the children inside a Link but it is
178-
// required by ChipField, which uses a Chip from Material UI.
179-
// The Material UI Chip requires an onClick handler to behave like a clickable element.
180-
const handleClick = () => {};
184+
const DefaultChildComponent = ({ clickable }: { clickable?: boolean }) => (
185+
<Chip
186+
sx={{ cursor: 'inherit' }}
187+
size="small"
188+
label={<RecordRepresentation />}
189+
clickable={clickable}
190+
/>
191+
);

0 commit comments

Comments
 (0)
Please sign in to comment.