Skip to content

Commit

Permalink
feat: add ComponentType for actions, pagination, title, empty and asi…
Browse files Browse the repository at this point in the history
…de List props
  • Loading branch information
guilbill committed Feb 21, 2025
1 parent 63ddf92 commit abf98d3
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 22 deletions.
73 changes: 69 additions & 4 deletions packages/ra-ui-materialui/src/list/List.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Admin, AutocompleteInput } from 'react-admin';
import { Admin, AutocompleteInput, Pagination } from 'react-admin';

Check warning on line 2 in packages/ra-ui-materialui/src/list/List.stories.tsx

View workflow job for this annotation

GitHub Actions / typecheck

'Pagination' is defined but never used. Allowed unused vars must match /^_/u
import {
CustomRoutes,
Resource,
Expand All @@ -10,6 +10,8 @@ import {
import fakeRestDataProvider from 'ra-data-fakerest';
import { Box, Card, Typography, Button, Link as MuiLink } from '@mui/material';

import { Empty as DefaultEmpty } from './Empty';
import { Pagination as DefaultPagination } from './pagination';
import { List } from './List';
import { SimpleList } from './SimpleList';
import { ListActions } from './ListActions';
Expand Down Expand Up @@ -284,6 +286,23 @@ export const TitleElement = () => (
</TestMemoryRouter>
);

const TitleSpan = () => <span>Custom list title</span>;

export const TitleComponent = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
<Resource
name="books"
list={() => (
<List title={TitleSpan}>
<BookList />
</List>
)}
/>
</Admin>
</TestMemoryRouter>
);

export const TitleFalse = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
Expand Down Expand Up @@ -314,15 +333,30 @@ export const HasCreate = () => (
</TestMemoryRouter>
);

const AsideComponent = () => <Card sx={{ padding: 2 }}>Aside</Card>;
const AsideBlock = () => <Card sx={{ padding: 2 }}>Aside</Card>;

export const AsideElement = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
<Resource
name="books"
list={() => (
<List aside={<AsideBlock />}>
<BookList />
</List>
)}
/>
</Admin>
</TestMemoryRouter>
);

export const Aside = () => (
export const AsideComponent = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
<Resource
name="books"
list={() => (
<List aside={<AsideComponent />}>
<List aside={AsideBlock}>
<BookList />
</List>
)}
Expand Down Expand Up @@ -404,6 +438,22 @@ export const Empty = () => (
</TestMemoryRouter>
);

export const EmptyComponent = () => (
<TestMemoryRouter initialEntries={['/authors']}>
<Admin dataProvider={defaultDataProvider}>
<Resource
name="authors"
list={() => (
<List empty={DefaultEmpty}>
<span />
</List>
)}
create={() => <span />}
/>
</Admin>
</TestMemoryRouter>
);

export const EmptyPartialPagination = () => (
<TestMemoryRouter initialEntries={['/authors']}>
<Admin
Expand Down Expand Up @@ -432,6 +482,21 @@ export const EmptyPartialPagination = () => (
</TestMemoryRouter>
);

export const PaginationComponent = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
<Resource
name="books"
list={() => (
<List pagination={DefaultPagination}>
<BookList />
</List>
)}
/>
</Admin>
</TestMemoryRouter>
);

export const SX = () => (
<TestMemoryRouter initialEntries={['/books']}>
<Admin dataProvider={defaultDataProvider}>
Expand Down
46 changes: 28 additions & 18 deletions packages/ra-ui-materialui/src/list/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,18 @@ export const ListView = <RecordType extends RaRecord = any>(
<ListToolbar
className={ListClasses.actions}
filters={filters}
actions={getElement(actions)}
actions={getActionsElement(actions)}
/>
)}
<Content className={ListClasses.content}>{children}</Content>
{!error && pagination !== false && pagination}
{!error && pagination !== false && getElement(pagination)}
</div>
);

const renderEmpty = () =>
empty !== false && <div className={ListClasses.noResults}>{empty}</div>;
empty !== false && (
<div className={ListClasses.noResults}>{getElement(empty)}</div>
);

const shouldRenderEmptyPage =
!error &&
Expand All @@ -93,34 +95,42 @@ export const ListView = <RecordType extends RaRecord = any>(
<Root className={clsx('list-page', className)} {...rest}>
{title !== false && (
<Title
title={title}
title={title ? getTitleElement(title) : undefined}
defaultTitle={defaultTitle}
preferenceKey={`${resource}.list.title`}
/>
)}
{shouldRenderEmptyPage ? renderEmpty() : renderList()}
{aside}
{aside ? getElement(aside) : null}
</Root>
);
};

const getElement = (
ElementOrComponent: React.ComponentType<any> | ReactElement | false
) => {
if (ElementOrComponent === false) {
const getTitleElement = (title: ReactElement | ComponentType | string) => {
if (typeof title === 'string') {
return title;
}
return getElement(title);
};

const getActionsElement = (actions: ListViewProps['actions']) => {
if (!actions) {
return;
}
return getElement(actions);
};

const getElement = (ElementOrComponent: ComponentType | ReactElement) => {
if (isValidElement(ElementOrComponent)) {
console.log(`We have an element`);
return ElementOrComponent;
}

if (isValidElementType(ElementOrComponent)) {
console.log(`We have a component`);
const Element = ElementOrComponent as ComponentType<any>;
const Element = ElementOrComponent as ComponentType;
return <Element />;
}
throw new Error(
'the value prop should be a valid ReactElement or a valid React Component'
);
};

export interface ListViewProps {
Expand Down Expand Up @@ -157,7 +167,7 @@ export interface ListViewProps {
* </List>
* );
*/
actions?: ReactElement | React.ComponentType | false;
actions?: ReactElement | ComponentType | false;

/**
* The content to render as a sidebar.
Expand Down Expand Up @@ -185,7 +195,7 @@ export interface ListViewProps {
* </List>
* );
*/
aside?: ReactElement;
aside?: ReactElement | ComponentType;

/**
* A class name to apply to the root div element
Expand Down Expand Up @@ -256,7 +266,7 @@ export interface ListViewProps {
* </List>
* );
*/
empty?: ReactElement | false;
empty?: ReactElement | ComponentType | false;

/**
* Set to true to return null while the list is loading.
Expand Down Expand Up @@ -309,7 +319,7 @@ export interface ListViewProps {
* </List>
* );
*/
pagination?: ReactElement | false;
pagination?: ReactElement | ComponentType | false;

/**
* The page title (main title) to display above the data. Defaults to the humanized resource name.
Expand All @@ -324,7 +334,7 @@ export interface ListViewProps {
* </List>
* );
*/
title?: string | ReactElement | false;
title?: string | ReactElement | ComponentType | false;

/**
* The CSS styles to apply to the component.
Expand Down

0 comments on commit abf98d3

Please sign in to comment.