diff --git a/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.spec.tsx b/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.spec.tsx
index 5d5c107f7ca..4676dee5664 100644
--- a/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.spec.tsx
+++ b/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.spec.tsx
@@ -15,7 +15,11 @@ import { TextInput } from '../TextInput';
import { ArrayInput } from './ArrayInput';
import { SimpleFormIterator } from './SimpleFormIterator';
import { useFormContext } from 'react-hook-form';
-import { GlobalValidation, ValidationInFormTab } from './ArrayInput.stories';
+import {
+ GlobalValidation,
+ ScalarWithValidation,
+ ValidationInFormTab,
+} from './ArrayInput.stories';
describe('', () => {
it('should pass its record props to its child', async () => {
@@ -359,6 +363,19 @@ describe('', () => {
});
});
+ it('should correctly update validation state after removing an item', async () => {
+ render();
+
+ await screen.findByDisplayValue('classic');
+ fireEvent.click(await screen.findByLabelText('Add'));
+ fireEvent.click(await screen.findByText('Save'));
+ await screen.findByText('Required');
+ fireEvent.click((await screen.findAllByLabelText('Remove'))[0]);
+ await waitFor(() => {
+ expect(screen.queryByText('Required')).toBeNull();
+ });
+ });
+
describe('used within a form with global validation', () => {
it('should display an error if the array is required and empty', async () => {
render();
@@ -366,10 +383,15 @@ describe('', () => {
const RemoveButtons = screen.getAllByLabelText('Remove');
fireEvent.click(RemoveButtons[1]);
fireEvent.click(RemoveButtons[0]);
+ await waitFor(() => {
+ expect(screen.queryAllByLabelText('Remove')).toHaveLength(0);
+ });
const SaveButton = screen.getByText('Save');
fireEvent.click(SaveButton);
await screen.findByText(
- 'The form is not valid. Please check for errors'
+ 'The form is not valid. Please check for errors',
+ undefined,
+ { timeout: 3000 }
);
});
it('should display an error if one of the required field is empty', async () => {
diff --git a/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.stories.tsx b/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.stories.tsx
index b8b8f75d8ee..3eb82030a05 100644
--- a/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.stories.tsx
+++ b/packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.stories.tsx
@@ -199,6 +199,38 @@ export const Scalar = () => (
);
+export const ScalarWithValidation = () => (
+
+ (
+ {
+ console.log(data);
+ },
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ )}
+ />
+
+);
+
const order = {
id: 1,
date: '2022-08-30',
diff --git a/packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx b/packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx
index 3fc87585479..4b3e5263830 100644
--- a/packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx
+++ b/packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx
@@ -61,7 +61,7 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
} = props;
const [confirmIsOpen, setConfirmIsOpen] = useState(false);
const { append, fields, move, remove, replace } = useArrayInput(props);
- const { resetField } = useFormContext();
+ const { resetField, trigger, getValues } = useFormContext();
const translate = useTranslate();
const record = useRecordContext(props);
const initialDefaultValue = useRef({});
@@ -69,8 +69,16 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
const removeField = useCallback(
(index: number) => {
remove(index);
+ const isScalarArray = getValues(source).every(
+ (value: any) => typeof value !== 'object'
+ );
+ if (isScalarArray) {
+ // Trigger validation on the Array to avoid ghost errors.
+ // Otherwise, validation errors on removed fields might still be displayed
+ trigger(source);
+ }
},
- [remove]
+ [remove, trigger, source, getValues]
);
if (fields.length > 0) {