Skip to content

Commit 0f7d0e9

Browse files
committed
Fix BooleanInput initialValue overrides existing value from record
Closes #6511
1 parent 4845d99 commit 0f7d0e9

File tree

3 files changed

+147
-4
lines changed

3 files changed

+147
-4
lines changed

packages/ra-core/src/form/useInput.spec.tsx

+94-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { FunctionComponent, ReactElement } from 'react';
33
import { render, fireEvent } from '@testing-library/react';
4-
import { Form } from 'react-final-form';
4+
import { Form, useFormState } from 'react-final-form';
55
import FormWithRedirect from './FormWithRedirect';
66
import useInput, { InputProps } from './useInput';
77
import { required } from './validate';
@@ -235,4 +235,97 @@ describe('useInput', () => {
235235
);
236236
expect(queryByDisplayValue('99')).toBeNull();
237237
});
238+
239+
const BooleanInput = ({
240+
source,
241+
initialValue,
242+
}: {
243+
source: string;
244+
initialValue?: boolean;
245+
}) => (
246+
<Input
247+
source={source}
248+
initialValue={initialValue}
249+
type="checkbox"
250+
resource="posts"
251+
>
252+
{() => <BooleanInputValue source={source} />}
253+
</Input>
254+
);
255+
256+
const BooleanInputValue = ({ source }) => {
257+
const values = useFormState().values;
258+
return (
259+
<>
260+
{typeof values[source] === 'undefined'
261+
? 'undefined'
262+
: values[source]
263+
? 'true'
264+
: 'false'}
265+
</>
266+
);
267+
};
268+
269+
it('does not change the value if the field is of type checkbox and has no value', () => {
270+
const { queryByText } = renderWithRedux(
271+
<FormWithRedirect
272+
onSubmit={jest.fn()}
273+
record={{ id: 1 }}
274+
render={() => <BooleanInput source="is_published" />}
275+
/>
276+
);
277+
expect(queryByText('undefined')).not.toBeNull();
278+
});
279+
280+
it('applies the initialValue true when the field is of type checkbox and has no value', () => {
281+
const { queryByText } = renderWithRedux(
282+
<FormWithRedirect
283+
onSubmit={jest.fn()}
284+
record={{ id: 1 }}
285+
render={() => (
286+
<BooleanInput source="is_published" initialValue={true} />
287+
)}
288+
/>
289+
);
290+
expect(queryByText('true')).not.toBeNull();
291+
});
292+
293+
it('applies the initialValue false when the field is of type checkbox and has no value', () => {
294+
const { queryByText } = renderWithRedux(
295+
<FormWithRedirect
296+
onSubmit={jest.fn()}
297+
record={{ id: 1 }}
298+
render={() => (
299+
<BooleanInput source="is_published" initialValue={false} />
300+
)}
301+
/>
302+
);
303+
expect(queryByText('false')).not.toBeNull();
304+
});
305+
306+
it('does not apply the initialValue true when the field is of type checkbox and has a value', () => {
307+
const { queryByText } = renderWithRedux(
308+
<FormWithRedirect
309+
onSubmit={jest.fn()}
310+
record={{ id: 1, is_published: false }}
311+
render={() => (
312+
<BooleanInput source="is_published" initialValue={true} />
313+
)}
314+
/>
315+
);
316+
expect(queryByText('false')).not.toBeNull();
317+
});
318+
319+
it('does not apply the initialValue false when the field is of type checkbox and has a value', () => {
320+
const { queryByText } = renderWithRedux(
321+
<FormWithRedirect
322+
onSubmit={jest.fn()}
323+
record={{ id: 1, is_published: true }}
324+
render={() => (
325+
<BooleanInput source="is_published" initialValue={false} />
326+
)}
327+
/>
328+
);
329+
expect(queryByText('true')).not.toBeNull();
330+
});
238331
});

packages/ra-core/src/form/useInput.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -111,27 +111,42 @@ const useInput = ({
111111
[onFocus, customOnFocus]
112112
);
113113

114-
// Every time the record changes and didn't include a value for this field
115114
const form = useForm();
116115
const recordId = record?.id;
116+
// Every time the record changes and doesn't include a value for this field,
117+
// reset the field value to the initialValue (or defaultValue)
117118
useEffect(() => {
118-
if (input.value != null && input.value !== '') {
119+
if (
120+
typeof input.checked !== 'undefined' || // checkbox that has a value from record
121+
(input.value != null && input.value !== '') // any other input that has a value from record
122+
) {
123+
// no need to apply a default value
119124
return;
120125
}
126+
121127
// Apply the default value if provided
122128
// We use a change here which will make the form dirty but this is expected
123129
// and identical to what FinalForm does (https://final-form.org/docs/final-form/types/FieldConfig#defaultvalue)
124130
if (defaultValue != null) {
125131
form.change(source, defaultValue);
126132
}
127133

134+
// apply initial value if provided
128135
if (initialValue != null) {
129136
form.batch(() => {
130137
form.change(source, initialValue);
131138
form.resetFieldState(source);
132139
});
133140
}
134-
}, [recordId, input.value, defaultValue, initialValue, source, form]);
141+
}, [
142+
recordId,
143+
input.value,
144+
input.checked,
145+
defaultValue,
146+
initialValue,
147+
source,
148+
form,
149+
]);
135150

136151
// If there is an input prop, this input has already been enhanced by final-form
137152
// This is required in for inputs used inside other inputs (such as the SelectInput inside a ReferenceInput)

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

+35
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,41 @@ describe('<BooleanInput />', () => {
7272
expect(input.checked).toBe(false);
7373
});
7474

75+
it('should be checked if the value is undefined and initialValue is true', () => {
76+
const { getByLabelText } = render(
77+
<Form
78+
onSubmit={jest.fn}
79+
render={() => (
80+
<BooleanInput {...defaultProps} initialValue={true} />
81+
)}
82+
/>
83+
);
84+
85+
const input = getByLabelText(
86+
'resources.posts.fields.isPublished'
87+
) as HTMLInputElement;
88+
89+
expect(input.checked).toBe(true);
90+
});
91+
92+
it('should be checked if the value is true and initialValue is false', () => {
93+
const { getByLabelText } = render(
94+
<Form
95+
onSubmit={jest.fn}
96+
initialValues={{ isPublished: true }}
97+
render={() => (
98+
<BooleanInput {...defaultProps} initialValue={false} />
99+
)}
100+
/>
101+
);
102+
103+
const input = getByLabelText(
104+
'resources.posts.fields.isPublished'
105+
) as HTMLInputElement;
106+
107+
expect(input.checked).toBe(true);
108+
});
109+
75110
it('should update on click', async () => {
76111
const { getByLabelText } = render(
77112
<Form

0 commit comments

Comments
 (0)