Skip to content

Commit a6dd50c

Browse files
authored
Merge pull request #7641 from marmelab/fix-simple-form-iterator-labels
Fix SimpleFormIterator Labels
2 parents dc6bce6 + 5496c72 commit a6dd50c

7 files changed

+63
-32
lines changed

packages/ra-core/src/util/getFieldLabelTranslationArgs.spec.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,22 @@ describe('getFieldLabelTranslationArgs', () => {
4343
]);
4444
});
4545

46-
it('should return the source and resource as translate key', () =>
46+
it('should return the source and resource as translate key', () => {
4747
expect(
4848
getFieldLabelTranslationArgs({
4949
resource: 'posts',
5050
source: 'title',
5151
})
52-
).toEqual([`resources.posts.fields.title`, { _: 'Title' }]));
52+
).toEqual([`resources.posts.fields.title`, { _: 'Title' }]);
53+
});
54+
55+
it('should accept use the parentSource to build the translation key if provided', () => {
56+
expect(
57+
getFieldLabelTranslationArgs({
58+
resource: 'posts',
59+
source: 'url',
60+
parentSource: 'backlinks',
61+
})
62+
).toEqual([`resources.posts.fields.backlinks.url`, { _: 'Url' }]);
63+
});
5364
});

packages/ra-core/src/util/getFieldLabelTranslationArgs.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import inflection from 'inflection';
22

33
interface Args {
44
label?: string;
5+
parentSource?: string;
56
resource?: string;
67
source?: string;
78
}
@@ -22,13 +23,14 @@ export default (options?: Args): TranslationArguments => {
2223
return [''];
2324
}
2425

25-
const { label, resource, source } = options;
26-
26+
const { label, parentSource, resource, source } = options;
2727
return typeof label !== 'undefined'
2828
? [label, { _: label }]
2929
: typeof source !== 'undefined'
3030
? [
31-
`resources.${resource}.fields.${source}`,
31+
`resources.${resource}.fields.${
32+
parentSource ? `${parentSource}.${source}` : source
33+
}`,
3234
{
3335
_: inflection.transform(source, ['underscore', 'humanize']),
3436
},

packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.spec.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,19 @@ describe('<ArrayInput />', () => {
116116
</AdminContext>
117117
);
118118
expect(
119-
screen.queryAllByLabelText('resources.bar.fields.id')
119+
screen.queryAllByLabelText('resources.bar.fields.arr.id')
120120
).toHaveLength(2);
121121
expect(
122122
screen
123-
.queryAllByLabelText('resources.bar.fields.id')
123+
.queryAllByLabelText('resources.bar.fields.arr.id')
124124
.map(input => (input as HTMLInputElement).value)
125125
).toEqual(['123', '456']);
126126
expect(
127-
screen.queryAllByLabelText('resources.bar.fields.foo')
127+
screen.queryAllByLabelText('resources.bar.fields.arr.foo')
128128
).toHaveLength(2);
129129
expect(
130130
screen
131-
.queryAllByLabelText('resources.bar.fields.foo')
131+
.queryAllByLabelText('resources.bar.fields.arr.foo')
132132
.map(input => (input as HTMLInputElement).value)
133133
).toEqual(['bar', 'baz']);
134134
});
@@ -169,7 +169,7 @@ describe('<ArrayInput />', () => {
169169
});
170170
fireEvent.click(screen.getByText('ra.action.add'));
171171
const firstId = screen.getAllByLabelText(
172-
'resources.bar.fields.id *'
172+
'resources.bar.fields.arr.id *'
173173
)[0];
174174
fireEvent.change(firstId, {
175175
target: { value: 'aaa' },
@@ -179,7 +179,7 @@ describe('<ArrayInput />', () => {
179179
});
180180
fireEvent.blur(firstId);
181181
const firstFoo = screen.getAllByLabelText(
182-
'resources.bar.fields.foo *'
182+
'resources.bar.fields.arr.foo *'
183183
)[0];
184184
fireEvent.change(firstFoo, {
185185
target: { value: 'aaa' },
@@ -195,7 +195,7 @@ describe('<ArrayInput />', () => {
195195
});
196196
});
197197

198-
it('should mantain its form value after having been unmounted', async () => {
198+
it('should maintain its form value after having been unmounted', async () => {
199199
let value, setArrayInputVisible;
200200

201201
const MyArrayInput = () => {

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.spec.tsx

+11-11
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('<SimpleFormIterator />', () => {
4848
</Wrapper>
4949
);
5050
const inputElements = screen.queryAllByLabelText(
51-
'resources.undefined.fields.email'
51+
'resources.undefined.fields.emails.email'
5252
);
5353
expect(inputElements).toHaveLength(2);
5454
expect((inputElements[0] as HTMLInputElement).disabled).toBeFalsy();
@@ -75,7 +75,7 @@ describe('<SimpleFormIterator />', () => {
7575
</Wrapper>
7676
);
7777
const inputElements = screen.queryAllByLabelText(
78-
'resources.undefined.fields.email'
78+
'resources.undefined.fields.emails.email'
7979
);
8080
expect(inputElements).toHaveLength(2);
8181
expect((inputElements[0] as HTMLInputElement).disabled).toBeTruthy();
@@ -102,7 +102,7 @@ describe('<SimpleFormIterator />', () => {
102102
</Wrapper>
103103
);
104104
const inputElements = screen.queryAllByLabelText(
105-
'resources.undefined.fields.email'
105+
'resources.undefined.fields.emails.email'
106106
);
107107
expect(inputElements).toHaveLength(2);
108108
expect((inputElements[0] as HTMLInputElement).disabled).toBeTruthy();
@@ -221,7 +221,7 @@ describe('<SimpleFormIterator />', () => {
221221
fireEvent.click(addItemElement);
222222
await waitFor(() => {
223223
const inputElements = screen.queryAllByLabelText(
224-
'resources.undefined.fields.email'
224+
'resources.undefined.fields.emails.email'
225225
);
226226

227227
expect(inputElements.length).toBe(1);
@@ -230,14 +230,14 @@ describe('<SimpleFormIterator />', () => {
230230
fireEvent.click(addItemElement);
231231
await waitFor(() => {
232232
const inputElements = screen.queryAllByLabelText(
233-
'resources.undefined.fields.email'
233+
'resources.undefined.fields.emails.email'
234234
);
235235

236236
expect(inputElements.length).toBe(2);
237237
});
238238

239239
const inputElements = screen.queryAllByLabelText(
240-
'resources.undefined.fields.email'
240+
'resources.undefined.fields.emails.email'
241241
) as HTMLInputElement[];
242242

243243
expect(
@@ -339,7 +339,7 @@ describe('<SimpleFormIterator />', () => {
339339
);
340340

341341
const inputElements = screen.queryAllByLabelText(
342-
'resources.undefined.fields.email'
342+
'resources.undefined.fields.emails.email'
343343
) as HTMLInputElement[];
344344

345345
expect(
@@ -356,7 +356,7 @@ describe('<SimpleFormIterator />', () => {
356356
fireEvent.click(removeFirstButton);
357357
await waitFor(() => {
358358
const inputElements = screen.queryAllByLabelText(
359-
'resources.undefined.fields.email'
359+
'resources.undefined.fields.emails.email'
360360
) as HTMLInputElement[];
361361

362362
expect(
@@ -383,7 +383,7 @@ describe('<SimpleFormIterator />', () => {
383383
);
384384

385385
const inputElements = screen.queryAllByLabelText(
386-
'resources.undefined.fields.email'
386+
'resources.undefined.fields.emails.email'
387387
) as HTMLInputElement[];
388388

389389
expect(
@@ -399,7 +399,7 @@ describe('<SimpleFormIterator />', () => {
399399
fireEvent.click(moveDownFirstButton[0]);
400400
await waitFor(() => {
401401
const inputElements = screen.queryAllByLabelText(
402-
'resources.undefined.fields.email'
402+
'resources.undefined.fields.emails.email'
403403
) as HTMLInputElement[];
404404

405405
expect(
@@ -414,7 +414,7 @@ describe('<SimpleFormIterator />', () => {
414414
fireEvent.click(moveUpButton[1]);
415415
await waitFor(() => {
416416
const inputElements = screen.queryAllByLabelText(
417-
'resources.undefined.fields.email'
417+
'resources.undefined.fields.emails.email'
418418
) as HTMLInputElement[];
419419

420420
expect(

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
8686
add: addField,
8787
remove: removeField,
8888
reOrder: handleReorder,
89+
source,
8990
}),
90-
[fields.length, addField, removeField, handleReorder]
91+
[addField, fields.length, handleReorder, removeField, source]
9192
);
9293
return fields ? (
9394
<SimpleFormIteratorContext.Provider value={context}>

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIteratorContext.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ export const SimpleFormIteratorContext = createContext<
1111
>(undefined);
1212

1313
export type SimpleFormIteratorContextValue = {
14-
total: number;
1514
add: () => void;
1615
remove: (index: number) => void;
1716
reOrder: (index: number, newIndex: number) => void;
17+
source: string;
18+
total: number;
1819
};

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIteratorItem.tsx

+23-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from 'react';
1212
import { Typography } from '@mui/material';
1313
import clsx from 'clsx';
14-
import { RaRecord } from 'ra-core';
14+
import { getFieldLabelTranslationArgs, RaRecord, useTranslate } from 'ra-core';
1515

1616
import { SimpleFormIteratorClasses } from './useSimpleFormIteratorStyles';
1717
import { useSimpleFormIterator } from './useSimpleFormIterator';
@@ -38,7 +38,13 @@ export const SimpleFormIteratorItem = React.forwardRef(
3838
source,
3939
} = props;
4040

41-
const { total, reOrder, remove } = useSimpleFormIterator();
41+
const translate = useTranslate();
42+
const {
43+
total,
44+
reOrder,
45+
remove,
46+
source: parentSource,
47+
} = useSimpleFormIterator();
4248
// Returns a boolean to indicate whether to disable the remove button for certain fields.
4349
// If disableRemove is a function, then call the function with the current record to
4450
// determining if the button should be disabled. Otherwise, use a boolean property that
@@ -111,11 +117,21 @@ export const SimpleFormIteratorItem = React.forwardRef(
111117
: member,
112118
index: source ? undefined : index2,
113119
label:
114-
typeof input.props.label === 'undefined'
115-
? source
116-
? `resources.${resource}.fields.${source}`
117-
: undefined
118-
: input.props.label,
120+
input.props.label === '' ||
121+
input.props.label === false
122+
? input.props.label
123+
: // We can't rely on the default label inference done in the input by FieldTitle because
124+
// at the time it renders, its source will be something like `arraySource.0.inputSource`
125+
// and inference will fail
126+
translate(
127+
...getFieldLabelTranslationArgs(
128+
{
129+
parentSource,
130+
resource,
131+
source,
132+
}
133+
)
134+
),
119135
disabled,
120136
...inputProps,
121137
});

0 commit comments

Comments
 (0)