Skip to content

Commit c693cf8

Browse files
authored
Merge pull request #10771 from marmelab/override-props
Override main views and buttons styles and props using MUI themes
2 parents 3ecc932 + 669995b commit c693cf8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1352
-180
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import expect from 'expect';
4+
5+
import { Themed } from './BulkDeleteButton.stories';
6+
7+
describe('<BulkDeleteButton />', () => {
8+
it('should be customized by a theme', async () => {
9+
render(<Themed />);
10+
11+
const button = await screen.findByTestId('themed');
12+
expect(button.textContent).toBe('Bulk Delete');
13+
expect(button.classList).toContain('custom-class');
14+
});
15+
});
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import React from 'react';
2+
import { ThemeOptions } from '@mui/material';
3+
import { deepmerge } from '@mui/utils';
4+
import { Resource } from 'ra-core';
5+
import polyglotI18nProvider from 'ra-i18n-polyglot';
6+
import englishMessages from 'ra-language-english';
7+
import fakeRestDataProvider from 'ra-data-fakerest';
8+
9+
import { AdminContext } from '../AdminContext';
10+
import { BulkDeleteButton } from './BulkDeleteButton';
11+
import { defaultLightTheme } from '../theme';
12+
import { Datagrid, List } from '../list';
13+
import { NumberField, TextField } from '../field';
14+
import { AdminUI } from '../AdminUI';
15+
16+
export default { title: 'ra-ui-materialui/button/BulkDeleteButton' };
17+
18+
const i18nProvider = polyglotI18nProvider(
19+
() => englishMessages,
20+
'en' // Default locale
21+
);
22+
23+
const dataProvider = fakeRestDataProvider({
24+
books: [
25+
{
26+
id: 1,
27+
title: 'War and Peace',
28+
author: 'Leo Tolstoy',
29+
reads: 23,
30+
},
31+
{
32+
id: 2,
33+
title: 'Pride and Predjudice',
34+
author: 'Jane Austen',
35+
reads: 854,
36+
},
37+
{
38+
id: 3,
39+
title: 'The Picture of Dorian Gray',
40+
author: 'Oscar Wilde',
41+
reads: 126,
42+
},
43+
{
44+
id: 4,
45+
title: 'Le Petit Prince',
46+
author: 'Antoine de Saint-Exupéry',
47+
reads: 86,
48+
},
49+
{
50+
id: 5,
51+
title: "Alice's Adventures in Wonderland",
52+
author: 'Lewis Carroll',
53+
reads: 125,
54+
},
55+
{
56+
id: 6,
57+
title: 'Madame Bovary',
58+
author: 'Gustave Flaubert',
59+
reads: 452,
60+
},
61+
{
62+
id: 7,
63+
title: 'The Lord of the Rings',
64+
author: 'J. R. R. Tolkien',
65+
reads: 267,
66+
},
67+
{
68+
id: 8,
69+
title: "Harry Potter and the Philosopher's Stone",
70+
author: 'J. K. Rowling',
71+
reads: 1294,
72+
},
73+
{
74+
id: 9,
75+
title: 'The Alchemist',
76+
author: 'Paulo Coelho',
77+
reads: 23,
78+
},
79+
{
80+
id: 10,
81+
title: 'A Catcher in the Rye',
82+
author: 'J. D. Salinger',
83+
reads: 209,
84+
},
85+
{
86+
id: 11,
87+
title: 'Ulysses',
88+
author: 'James Joyce',
89+
reads: 12,
90+
},
91+
],
92+
authors: [],
93+
});
94+
95+
const Wrapper = ({ children, ...props }) => {
96+
return (
97+
<AdminContext
98+
dataProvider={dataProvider}
99+
i18nProvider={i18nProvider}
100+
{...props}
101+
>
102+
<AdminUI>
103+
<Resource
104+
name="books"
105+
list={() => (
106+
<List>
107+
<Datagrid bulkActionButtons={children}>
108+
<TextField source="id" />
109+
<TextField source="title" />
110+
<TextField source="author" />
111+
<NumberField source="reads" />
112+
</Datagrid>
113+
</List>
114+
)}
115+
/>
116+
</AdminUI>
117+
</AdminContext>
118+
);
119+
};
120+
121+
export const Basic = () => {
122+
return (
123+
<Wrapper>
124+
<BulkDeleteButton />
125+
</Wrapper>
126+
);
127+
};
128+
129+
export const Themed = () => {
130+
return (
131+
<Wrapper
132+
theme={deepmerge(defaultLightTheme, {
133+
components: {
134+
RaBulkDeleteButton: {
135+
defaultProps: {
136+
label: 'Bulk Delete',
137+
mutationMode: 'optimistic',
138+
className: 'custom-class',
139+
'data-testid': 'themed',
140+
},
141+
},
142+
},
143+
} as ThemeOptions)}
144+
>
145+
<BulkDeleteButton />
146+
</Wrapper>
147+
);
148+
};

packages/ra-ui-materialui/src/button/BulkDeleteButton.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import * as React from 'react';
2+
import { MutationMode, useCanAccess, useResourceContext } from 'ra-core';
3+
import { useThemeProps } from '@mui/material/styles';
4+
25
import {
36
BulkDeleteWithConfirmButton,
47
BulkDeleteWithConfirmButtonProps,
@@ -7,7 +10,6 @@ import {
710
BulkDeleteWithUndoButton,
811
BulkDeleteWithUndoButtonProps,
912
} from './BulkDeleteWithUndoButton';
10-
import { MutationMode, useCanAccess, useResourceContext } from 'ra-core';
1113

1214
/**
1315
* Deletes the selected rows.
@@ -32,10 +34,12 @@ import { MutationMode, useCanAccess, useResourceContext } from 'ra-core';
3234
* </List>
3335
* );
3436
*/
35-
export const BulkDeleteButton = ({
36-
mutationMode = 'undoable',
37-
...props
38-
}: BulkDeleteButtonProps) => {
37+
export const BulkDeleteButton = (inProps: BulkDeleteButtonProps) => {
38+
const { mutationMode = 'undoable', ...props } = useThemeProps({
39+
name: PREFIX,
40+
props: inProps,
41+
});
42+
3943
const resource = useResourceContext(props);
4044
if (!resource) {
4145
throw new Error(
@@ -62,3 +66,17 @@ interface Props {
6266

6367
export type BulkDeleteButtonProps = Props &
6468
(BulkDeleteWithUndoButtonProps | BulkDeleteWithConfirmButtonProps);
69+
70+
const PREFIX = 'RaBulkDeleteButton';
71+
72+
declare module '@mui/material/styles' {
73+
interface ComponentsPropsList {
74+
[PREFIX]: Partial<BulkDeleteButtonProps>;
75+
}
76+
77+
interface Components {
78+
[PREFIX]?: {
79+
defaultProps?: ComponentsPropsList[typeof PREFIX];
80+
};
81+
}
82+
}

packages/ra-ui-materialui/src/button/BulkExportButton.spec.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import {
99
import { createTheme, ThemeProvider } from '@mui/material/styles';
1010

1111
import { BulkExportButton } from './BulkExportButton';
12+
import { Themed } from './BulkExportButton.stories';
1213

1314
const theme = createTheme();
1415

1516
describe('<BulkExportButton />', () => {
16-
it('should invoke dataProvider with meta', async () => {
17-
const exporter = jest.fn().mockName('exporter');
18-
const dataProvider = testDataProvider({
19-
getMany: jest.fn().mockResolvedValueOnce({ data: [], total: 0 }),
20-
});
17+
const exporter = jest.fn().mockName('exporter');
18+
const dataProvider = testDataProvider({
19+
getMany: jest.fn().mockResolvedValueOnce({ data: [], total: 0 }),
20+
});
2121

22+
it('should invoke dataProvider with meta', async () => {
2223
render(
2324
<CoreAdminContext dataProvider={dataProvider}>
2425
<ThemeProvider theme={theme}>
@@ -46,4 +47,12 @@ describe('<BulkExportButton />', () => {
4647
expect(exporter).toHaveBeenCalled();
4748
});
4849
});
50+
51+
it('should be customized by a theme', async () => {
52+
render(<Themed />);
53+
54+
const button = await screen.findByTestId('themed');
55+
expect(button.textContent).toBe('Bulk Export');
56+
expect(button.classList).toContain('custom-class');
57+
});
4958
});

0 commit comments

Comments
 (0)