Skip to content

Commit 81f3266

Browse files
committed
Support resource specific translations in ListButton
1 parent e85023e commit 81f3266

File tree

4 files changed

+143
-14
lines changed

4 files changed

+143
-14
lines changed

docs/Buttons.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,12 +1237,42 @@ export const PostShow = () => (
12371237
| Prop | Required | Type | Default | Description |
12381238
| ------------- | -------- | --------------- | ---------------- | ---------------------------------------------- |
12391239
| `resource` | Optional | `string` | - | target resource, e.g. 'posts' |
1240-
| `label` | Optional | `string` | 'ra.action.list' | label or translation message to use |
1240+
| `label` | Optional | `string` | - | label or translation message to use |
12411241
| `icon` | Optional | `ReactElement` | - | iconElement, e.g. `<CommentIcon />` |
12421242
| `scrollToTop` | Optional | `boolean` | `true` | Scroll to top after link |
12431243

12441244
It also supports [all the other `<Button>` props](#button).
12451245

1246+
### `label`
1247+
1248+
By default, the label is `List` in English. In other languages, it's the translation of the `'ra.action.list'` key.
1249+
1250+
You can customize this label by providing a resource specific translation with the key `resources.RESOURCE.action.list` (e.g. `resources.posts.action.list`):
1251+
1252+
```js
1253+
// in src/i18n/en.js
1254+
import englishMessages from 'ra-language-english';
1255+
1256+
export const en = {
1257+
...englishMessages,
1258+
resources: {
1259+
posts: {
1260+
name: 'Post |||| Posts',
1261+
action: {
1262+
list: 'See all posts'
1263+
}
1264+
},
1265+
},
1266+
...
1267+
};
1268+
```
1269+
1270+
You can also customize this label by specifying a custom `label` prop:
1271+
1272+
```jsx
1273+
<ListButton label="Delete this comment" />
1274+
```
1275+
12461276
### `scrollToTop`
12471277

12481278
By default, `<ListButton>` scrolls the page to the top after redirecting. You can disable it as follows:

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { fireEvent, render, screen } from '@testing-library/react';
33
import expect from 'expect';
4-
import { Basic, AccessControl } from './ListButton.stories';
4+
import { Basic, AccessControl, Label } from './ListButton.stories';
55

66
const invalidButtonDomProps = {
77
redirect: 'list',
@@ -23,7 +23,23 @@ describe('<ListButton />', () => {
2323
spy.mockRestore();
2424
});
2525

26-
it('should only render when users have the right to create', async () => {
26+
it('should provide a default label', async () => {
27+
render(<Label translations="default" />);
28+
await screen.findByText('List');
29+
fireEvent.click(screen.getByText('English', { selector: 'button' }));
30+
fireEvent.click(await screen.findByText('Français'));
31+
await screen.findByText('Liste');
32+
});
33+
34+
it('should allow resource specific default title', async () => {
35+
render(<Label translations="resource specific" />);
36+
await screen.findByText('See all books');
37+
fireEvent.click(screen.getByText('English', { selector: 'button' }));
38+
fireEvent.click(await screen.findByText('Français'));
39+
await screen.findByText('Voir tous les livres');
40+
});
41+
42+
it('should only render when users have the right to list', async () => {
2743
render(<AccessControl />);
2844
await screen.findByDisplayValue('War and Peace');
2945
expect(screen.queryByLabelText('List')).toBeNull();

packages/ra-ui-materialui/src/button/ListButton.stories.tsx

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import englishMessages from 'ra-language-english';
44
import frenchMessages from 'ra-language-french';
55
import {
66
AuthProvider,
7+
I18nProvider,
8+
mergeTranslations,
79
RecordContextProvider,
810
Resource,
911
ResourceContextProvider,
@@ -21,16 +23,51 @@ import { TextInput } from '../input/TextInput';
2123
import { ListButton } from './ListButton';
2224
import { Edit } from '../detail/Edit';
2325
import { TopToolbar } from '../layout';
26+
import { LocalesMenuButton } from './LocalesMenuButton';
2427

2528
export default { title: 'ra-ui-materialui/button/ListButton' };
2629

30+
const defaultI18nProvider = () =>
31+
polyglotI18nProvider(
32+
locale => (locale === 'fr' ? frenchMessages : englishMessages),
33+
'en',
34+
[
35+
{ locale: 'en', name: 'English' },
36+
{ locale: 'fr', name: 'Français' },
37+
]
38+
);
39+
40+
const customI18nProvider = polyglotI18nProvider(
41+
locale =>
42+
locale === 'fr'
43+
? mergeTranslations(frenchMessages, {
44+
resources: {
45+
books: {
46+
action: {
47+
list: 'Voir tous les livres',
48+
},
49+
},
50+
},
51+
})
52+
: mergeTranslations(englishMessages, {
53+
resources: {
54+
books: {
55+
action: {
56+
list: 'See all books',
57+
},
58+
},
59+
},
60+
}),
61+
'en',
62+
[
63+
{ locale: 'en', name: 'English' },
64+
{ locale: 'fr', name: 'Français' },
65+
]
66+
);
67+
2768
export const Basic = ({ buttonProps }: { buttonProps?: any }) => (
2869
<TestMemoryRouter>
29-
<AdminContext
30-
i18nProvider={polyglotI18nProvider(locale =>
31-
locale === 'fr' ? frenchMessages : englishMessages
32-
)}
33-
>
70+
<AdminContext i18nProvider={defaultI18nProvider()}>
3471
<ResourceContextProvider value="books">
3572
<RecordContextProvider value={{ id: 1 }}>
3673
<ListButton {...buttonProps} />
@@ -40,6 +77,39 @@ export const Basic = ({ buttonProps }: { buttonProps?: any }) => (
4077
</TestMemoryRouter>
4178
);
4279

80+
export const Label = ({
81+
translations = 'default',
82+
i18nProvider = translations === 'default'
83+
? defaultI18nProvider()
84+
: customI18nProvider,
85+
label,
86+
}: {
87+
i18nProvider?: I18nProvider;
88+
translations?: 'default' | 'resource specific';
89+
label?: string;
90+
}) => (
91+
<TestMemoryRouter>
92+
<AdminContext dataProvider={dataProvider} i18nProvider={i18nProvider}>
93+
<ResourceContextProvider value="books">
94+
<div>
95+
<ListButton label={label} />
96+
</div>
97+
<LocalesMenuButton />
98+
</ResourceContextProvider>
99+
</AdminContext>
100+
</TestMemoryRouter>
101+
);
102+
103+
Label.args = {
104+
translations: 'default',
105+
};
106+
Label.argTypes = {
107+
translations: {
108+
options: ['default', 'resource specific'],
109+
control: { type: 'radio' },
110+
},
111+
};
112+
43113
export const AccessControl = () => {
44114
const queryClient = new QueryClient();
45115

@@ -76,9 +146,7 @@ const AccessControlAdmin = ({ queryClient }: { queryClient: QueryClient }) => {
76146
<AdminContext
77147
dataProvider={dataProvider}
78148
authProvider={authProvider}
79-
i18nProvider={polyglotI18nProvider(locale =>
80-
locale === 'fr' ? frenchMessages : englishMessages
81-
)}
149+
i18nProvider={defaultI18nProvider()}
82150
queryClient={queryClient}
83151
>
84152
<AdminUI

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import * as React from 'react';
22
import ActionList from '@mui/icons-material/List';
33
import { Link } from 'react-router-dom';
4-
import { useResourceContext, useCreatePath, useCanAccess } from 'ra-core';
4+
import {
5+
useResourceContext,
6+
useCreatePath,
7+
useCanAccess,
8+
useTranslate,
9+
useGetResourceLabel,
10+
} from 'ra-core';
511

612
import { Button, ButtonProps } from './Button';
713

@@ -34,7 +40,7 @@ import { Button, ButtonProps } from './Button';
3440
export const ListButton = (props: ListButtonProps) => {
3541
const {
3642
icon = defaultIcon,
37-
label = 'ra.action.list',
43+
label: labelProp,
3844
resource: resourceProp,
3945
scrollToTop = true,
4046
...rest
@@ -50,11 +56,20 @@ export const ListButton = (props: ListButtonProps) => {
5056
resource,
5157
});
5258
const createPath = useCreatePath();
53-
59+
const translate = useTranslate();
60+
const getResourceLabel = useGetResourceLabel();
5461
if (!canAccess || isPending) {
5562
return null;
5663
}
5764

65+
const label =
66+
labelProp ??
67+
translate(`resources.${resource}.action.list`, {
68+
_: translate(`ra.action.list`, {
69+
name: getResourceLabel(resource, 1),
70+
}),
71+
});
72+
5873
return (
5974
<Button
6075
component={Link}

0 commit comments

Comments
 (0)