diff --git a/packages/ra-ui-materialui/src/input/FileInput.js b/packages/ra-ui-materialui/src/input/FileInput.js
index d56282608e4..ab08a410568 100644
--- a/packages/ra-ui-materialui/src/input/FileInput.js
+++ b/packages/ra-ui-materialui/src/input/FileInput.js
@@ -4,6 +4,7 @@ import { shallowEqual } from 'recompose';
import Dropzone from 'react-dropzone';
import compose from 'recompose/compose';
import { withStyles } from '@material-ui/core/styles';
+import FormHelperText from '@material-ui/core/FormHelperText';
import classnames from 'classnames';
import { addField, translate } from 'ra-core';
@@ -83,9 +84,10 @@ export class FileInput extends Component {
this.setState({ files: updatedFiles });
if (this.props.multiple) {
- this.props.input.onChange(updatedFiles);
+ // Use onBlur to ensure redux-form set the input as touched
+ this.props.input.onBlur(updatedFiles);
} else {
- this.props.input.onChange(updatedFiles[0]);
+ this.props.input.onBlur(updatedFiles[0]);
}
};
@@ -96,10 +98,11 @@ export class FileInput extends Component {
this.setState({ files: filteredFiles });
+ // Use onBlur to ensure redux-form set the input as touched
if (this.props.multiple) {
- this.props.input.onChange(filteredFiles);
+ this.props.input.onBlur(filteredFiles);
} else {
- this.props.input.onChange(null);
+ this.props.input.onBlur(null);
}
};
@@ -155,10 +158,12 @@ export class FileInput extends Component {
isRequired,
label,
maxSize,
+ meta,
minSize,
multiple,
resource,
source,
+ translate,
options = {},
...rest
} = this.props;
@@ -171,6 +176,7 @@ export class FileInput extends Component {
source={source}
resource={resource}
isRequired={isRequired}
+ meta={meta}
{...sanitizeRestProps(rest)}
>
@@ -204,6 +210,13 @@ export class FileInput extends Component {
))}
)}
+ {meta &&
+ meta.touched &&
+ meta.error && (
+
+ {translate(meta.error)}
+
+ )}
);
diff --git a/packages/ra-ui-materialui/src/input/FileInput.spec.js b/packages/ra-ui-materialui/src/input/FileInput.spec.js
index 9113132d336..3853baede6d 100644
--- a/packages/ra-ui-materialui/src/input/FileInput.spec.js
+++ b/packages/ra-ui-materialui/src/input/FileInput.spec.js
@@ -34,7 +34,7 @@ describe('', () => {
});
it('should correctly update upon drop when allowing a single file', () => {
- const onChange = jest.fn();
+ const onBlur = jest.fn();
const wrapper = shallow(
', () => {
value: {
src: 'b64_picture',
},
- onChange,
+ onBlur,
}}
translate={x => x}
source="src"
@@ -51,13 +51,13 @@ describe('', () => {
wrapper.instance().onDrop([{ preview: 'new_b64_picture' }]);
- assert.deepEqual(onChange.mock.calls[0][0], {
+ assert.deepEqual(onBlur.mock.calls[0][0], {
preview: 'new_b64_picture',
});
});
it('should correctly update upon removal when allowing a single file', () => {
- const onChange = jest.fn();
+ const onBlur = jest.fn();
const wrapper = shallow(
', () => {
value: {
src: 'b64_picture',
},
- onChange,
+ onBlur,
}}
translate={x => x}
source="src"
@@ -73,11 +73,11 @@ describe('', () => {
);
wrapper.instance().onRemove({ src: 'b64_picture' })();
- assert.deepEqual(onChange.mock.calls[0][0], null);
+ assert.deepEqual(onBlur.mock.calls[0][0], null);
});
it('should correctly update upon drop when allowing multiple files', () => {
- const onChange = jest.fn();
+ const onBlur = jest.fn();
const wrapper = shallow(
', () => {
{ src: 'b64_picture' },
{ src: 'another_b64_picture' },
],
- onChange,
+ onBlur,
}}
translate={x => x}
source="pictures"
@@ -96,7 +96,7 @@ describe('', () => {
wrapper.instance().onDrop([{ preview: 'new_b64_picture' }]);
- assert.deepEqual(onChange.mock.calls[0][0], [
+ assert.deepEqual(onBlur.mock.calls[0][0], [
{ src: 'b64_picture' },
{ src: 'another_b64_picture' },
{ preview: 'new_b64_picture' },
@@ -104,7 +104,7 @@ describe('', () => {
});
it('should correctly update upon removal when allowing multiple files', () => {
- const onChange = jest.fn();
+ const onBlur = jest.fn();
const wrapper = shallow(
', () => {
{ src: 'b64_picture' },
{ src: 'another_b64_picture' },
],
- onChange,
+ onBlur,
}}
translate={x => x}
source="pictures"
@@ -123,7 +123,7 @@ describe('', () => {
wrapper.instance().onRemove({ src: 'another_b64_picture' })();
- assert.deepEqual(onChange.mock.calls[0][0], [{ src: 'b64_picture' }]);
+ assert.deepEqual(onBlur.mock.calls[0][0], [{ src: 'b64_picture' }]);
});
it('should display correct label depending multiple property', () => {
@@ -309,7 +309,7 @@ describe('', () => {
source="picture"
translate={x => x}
input={{
- onChange: () => {},
+ onBlur: () => {},
value: [
{ url: 'http://static.acme.com/foo.jpg' },
{ url: 'http://static.acme.com/bar.jpg' },
diff --git a/packages/ra-ui-materialui/src/input/ImageInput.spec.js b/packages/ra-ui-materialui/src/input/ImageInput.spec.js
index 5c0342c1205..d687f384eb9 100644
--- a/packages/ra-ui-materialui/src/input/ImageInput.spec.js
+++ b/packages/ra-ui-materialui/src/input/ImageInput.spec.js
@@ -216,7 +216,7 @@ describe('', () => {
source="picture"
translate={x => x}
input={{
- onChange: () => {},
+ onBlur: () => {},
value: [
{ url: 'http://static.acme.com/foo.jpg' },
{ url: 'http://static.acme.com/bar.jpg' },
diff --git a/packages/ra-ui-materialui/src/input/Labeled.js b/packages/ra-ui-materialui/src/input/Labeled.js
index 7d257cb62e0..6b152741914 100644
--- a/packages/ra-ui-materialui/src/input/Labeled.js
+++ b/packages/ra-ui-materialui/src/input/Labeled.js
@@ -67,7 +67,7 @@ export const Labeled = ({
className={className}
margin="normal"
fullWidth={fullWidth}
- error={meta && meta.touched && meta.error}
+ error={meta && meta.touched && !!meta.error}
>