Skip to content

Commit

Permalink
Merge pull request #9459 from marmelab/fix-file-input-is-touched
Browse files Browse the repository at this point in the history
Fix `<FileInput>` should display a validation errors right away when form mode is 'onChange'
  • Loading branch information
djhi authored Nov 20, 2023
2 parents 16d0c4e + 000bec3 commit a5fe992
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 2 deletions.
102 changes: 101 additions & 1 deletion packages/ra-ui-materialui/src/input/FileInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import {
waitFor,
waitForElementToBeRemoved,
} from '@testing-library/react';
import { testDataProvider } from 'ra-core';
import { required, testDataProvider } from 'ra-core';

import { AdminContext } from '../AdminContext';
import { SimpleForm, Toolbar } from '../form';
import { FileField, ImageField } from '../field';
import { FileInput } from './FileInput';
import { TextInput } from './TextInput';
import { SaveButton } from '../button';

describe('<FileInput />', () => {
Expand Down Expand Up @@ -469,6 +470,105 @@ describe('<FileInput />', () => {
test(<CustomLabel />, 'Custom label in component');
});

describe('Validation', () => {
it('should display a validation error if the value is required and there is no file', async () => {
const onSubmit = jest.fn();

render(
<AdminContext dataProvider={testDataProvider()}>
<SimpleForm onSubmit={onSubmit}>
<FileInput
{...defaultPropsMultiple}
validate={required()}
>
<FileField source="src" title="title" />
</FileInput>
<TextInput source="title" resource="posts" />
</SimpleForm>
</AdminContext>
);

fireEvent.change(
await screen.findByLabelText('resources.posts.fields.title'),
{
target: { value: 'Hello world!' },
}
);
fireEvent.click(screen.getByLabelText('ra.action.save'));

await screen.findByText('ra.validation.required');
expect(onSubmit).not.toHaveBeenCalled();
});

it('should display a validation error if the value is required and the file is removed', async () => {
const onSubmit = jest.fn();

render(
<AdminContext dataProvider={testDataProvider()}>
<SimpleForm
onSubmit={onSubmit}
defaultValues={{
images: [
{
src: 'test.png',
title: 'cats',
},
],
}}
>
<FileInput
{...defaultPropsMultiple}
validate={required()}
>
<FileField source="src" title="title" />
</FileInput>
</SimpleForm>
</AdminContext>
);

expect(screen.getByTitle('cats')).not.toBeNull();
fireEvent.click(screen.getAllByLabelText('ra.action.delete')[0]);
fireEvent.click(screen.getByLabelText('ra.action.save'));

await screen.findByText('ra.validation.required');
expect(onSubmit).not.toHaveBeenCalled();
});

it('should display a validation error right away when form mode is onChange', async () => {
const onSubmit = jest.fn();

render(
<AdminContext dataProvider={testDataProvider()}>
<SimpleForm
onSubmit={onSubmit}
defaultValues={{
images: [
{
src: 'test.png',
title: 'cats',
},
],
}}
mode="onChange"
>
<FileInput
{...defaultPropsMultiple}
validate={required()}
>
<FileField source="src" title="title" />
</FileInput>
</SimpleForm>
</AdminContext>
);

expect(screen.getByTitle('cats')).not.toBeNull();
fireEvent.click(screen.getAllByLabelText('ra.action.delete')[0]);

await screen.findByText('ra.validation.required');
expect(onSubmit).not.toHaveBeenCalled();
});
});

describe('Image Preview', () => {
it('should display file preview using child as preview component', () => {
render(
Expand Down
6 changes: 5 additions & 1 deletion packages/ra-ui-materialui/src/input/FileInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const FileInput = (props: FileInputProps) => {

const {
id,
field: { onChange, value },
field: { onChange, onBlur, value },
fieldState,
formState: { isSubmitted },
isRequired,
Expand All @@ -102,8 +102,10 @@ export const FileInput = (props: FileInputProps) => {

if (multiple) {
onChange(updatedFiles);
onBlur();
} else {
onChange(updatedFiles[0]);
onBlur();
}

if (onDropProp) {
Expand All @@ -124,8 +126,10 @@ export const FileInput = (props: FileInputProps) => {
stateFile => !shallowEqual(stateFile, file)
);
onChange(filteredFiles as any);
onBlur();
} else {
onChange(null);
onBlur();
}

if (onRemoveProp) {
Expand Down

0 comments on commit a5fe992

Please sign in to comment.