mirror of
https://github.com/strapi/strapi.git
synced 2025-08-01 05:17:14 +00:00
fix(files-required-validation): fix required prop save. add error display for component. fix validation file repeatedly
This commit is contained in:
parent
d4c2cb1009
commit
5a666d80df
@ -8,7 +8,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { cloneDeep, isArray, isObject } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
|
||||
import ImgPreview from 'components/ImgPreview';
|
||||
import InputFileDetails from 'components/InputFileDetails';
|
||||
@ -71,11 +72,12 @@ class InputFile extends React.Component {
|
||||
if (this.props.multiple) {
|
||||
value.splice(this.state.position, 1);
|
||||
}
|
||||
|
||||
// Update the parent's props
|
||||
const target = {
|
||||
name: this.props.name,
|
||||
type: 'file',
|
||||
value,
|
||||
value: Object.keys(value).length === 0 ? '' : value,
|
||||
};
|
||||
|
||||
this.props.onChange({ target });
|
||||
@ -94,6 +96,19 @@ class InputFile extends React.Component {
|
||||
this.setState({ position: newPosition });
|
||||
}
|
||||
|
||||
isVisibleDetails = () => {
|
||||
const {value} = this.props;
|
||||
|
||||
if (!value ||
|
||||
(isArray(value) && value.length === 0) ||
|
||||
(isObject(value) && Object.keys(value).length === 0)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
multiple,
|
||||
@ -104,39 +119,43 @@ class InputFile extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ImgPreview
|
||||
didDeleteFile={this.state.didDeleteFile}
|
||||
files={value}
|
||||
isUploading={this.state.isUploading}
|
||||
multiple={multiple}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
onBrowseClick={this.handleClick}
|
||||
onDrop={this.onDrop}
|
||||
position={this.state.position}
|
||||
updateFilePosition={this.updateFilePosition}
|
||||
/>
|
||||
<label style={{ width: '100%'}}>
|
||||
<input
|
||||
className={styles.inputFile}
|
||||
<div className={cn("form-control", styles.inputFileControlForm, this.props.error && 'is-invalid')}>
|
||||
<ImgPreview
|
||||
didDeleteFile={this.state.didDeleteFile}
|
||||
files={value}
|
||||
isUploading={this.state.isUploading}
|
||||
multiple={multiple}
|
||||
name={name}
|
||||
onChange={this.handleChange}
|
||||
type="file"
|
||||
ref={(input) => this.inputFile = input}
|
||||
onChange={onChange}
|
||||
onBrowseClick={this.handleClick}
|
||||
onDrop={this.onDrop}
|
||||
position={this.state.position}
|
||||
updateFilePosition={this.updateFilePosition}
|
||||
/>
|
||||
<label style={{"margin-bottom": 0, width: '100%'}}>
|
||||
<input
|
||||
className={styles.inputFile}
|
||||
multiple={multiple}
|
||||
name={name}
|
||||
onChange={this.handleChange}
|
||||
type="file"
|
||||
ref={(input) => this.inputFile = input}
|
||||
/>
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<i className="fa fa-plus" />
|
||||
<FormattedMessage id="app.components.InputFile.newFile" />
|
||||
</div>
|
||||
</label>
|
||||
<InputFileDetails
|
||||
file={value[this.state.position] || value[0] || value}
|
||||
multiple={multiple}
|
||||
number={value.length}
|
||||
onFileDelete={this.handleFileDelete}
|
||||
/>
|
||||
<div className={styles.buttonContainer}>
|
||||
<i className="fa fa-plus" />
|
||||
<FormattedMessage id="app.components.InputFile.newFile" />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
{this.isVisibleDetails() && (
|
||||
<InputFileDetails
|
||||
file={value[this.state.position] || value[0] || value}
|
||||
multiple={multiple}
|
||||
number={value.length}
|
||||
onFileDelete={this.handleFileDelete}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -146,9 +165,12 @@ InputFile.defaultProps = {
|
||||
multiple: false,
|
||||
setLabel: () => {},
|
||||
value: [],
|
||||
error: false,
|
||||
|
||||
};
|
||||
|
||||
InputFile.propTypes = {
|
||||
error: PropTypes.bool,
|
||||
multiple: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
@ -32,3 +32,7 @@
|
||||
.copy {
|
||||
cursor: copy !important;
|
||||
}
|
||||
|
||||
.inputFileControlForm {
|
||||
padding: 0;
|
||||
}
|
@ -14,22 +14,39 @@ import Label from 'components/Label';
|
||||
import InputDescription from 'components/InputDescription';
|
||||
import InputFile from 'components/InputFile';
|
||||
import InputSpacer from 'components/InputSpacer';
|
||||
import InputErrors from 'components/InputErrors';
|
||||
|
||||
// Styles
|
||||
import styles from './styles.scss';
|
||||
|
||||
class InputFileWithErrors extends React.Component {
|
||||
state = { label: false, hasValue: false };
|
||||
state = { errors: [], label: false, hasValue: false };
|
||||
|
||||
componentDidMount() {
|
||||
const { errors } = this.props;
|
||||
let newState = Object.assign({}, this.state);
|
||||
|
||||
if (this.props.multiple && !isEmpty(this.props.value)) {
|
||||
this.setState({ label: 1, hasValue: true });
|
||||
newState = Object.assign({}, newState, { label: 1, hasValue: true });
|
||||
}
|
||||
|
||||
if (!isEmpty(errors)) {
|
||||
newState = Object.assign({}, newState, { errors });
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.state.hasValue && !isEmpty(nextProps.value) && nextProps.multiple && differenceBy(nextProps.value, this.props.value, 'name').length > 0) {
|
||||
this.setState({ label: 1, hasValue: true });
|
||||
}
|
||||
// Check if errors have been updated during validations
|
||||
if (nextProps.didCheckErrors !== this.props.didCheckErrors) {
|
||||
// Remove from the state the errors that have already been set
|
||||
const errors = isEmpty(nextProps.errors) ? [] : nextProps.errors;
|
||||
this.setState({ errors });
|
||||
}
|
||||
}
|
||||
|
||||
setLabel = (label) => {
|
||||
@ -40,6 +57,9 @@ class InputFileWithErrors extends React.Component {
|
||||
const {
|
||||
className,
|
||||
customBootstrapClass,
|
||||
errorsClassName,
|
||||
errorsStyle,
|
||||
noErrorsDescription,
|
||||
inputDescription,
|
||||
inputDescriptionClassName,
|
||||
inputDescriptionStyle,
|
||||
@ -76,6 +96,7 @@ class InputFileWithErrors extends React.Component {
|
||||
)}
|
||||
<InputFile
|
||||
multiple={multiple}
|
||||
error={!isEmpty(this.state.errors)}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
setLabel={this.setLabel}
|
||||
@ -86,6 +107,11 @@ class InputFileWithErrors extends React.Component {
|
||||
message={inputDescription}
|
||||
style={inputDescriptionStyle}
|
||||
/>
|
||||
<InputErrors
|
||||
className={errorsClassName}
|
||||
errors={!noErrorsDescription && this.state.errors || []}
|
||||
style={errorsStyle}
|
||||
/>
|
||||
{spacer}
|
||||
</div>
|
||||
);
|
||||
@ -93,8 +119,12 @@ class InputFileWithErrors extends React.Component {
|
||||
}
|
||||
|
||||
InputFileWithErrors.defaultProps = {
|
||||
errors: [],
|
||||
errorsClassName: '',
|
||||
errorsStyle: {},
|
||||
className: '',
|
||||
customBootstrapClass: 'col-md-6',
|
||||
didCheckErrors: false,
|
||||
inputDescription: '',
|
||||
inputDescriptionClassName: '',
|
||||
inputDescriptionStyle: {},
|
||||
@ -102,6 +132,7 @@ InputFileWithErrors.defaultProps = {
|
||||
labelClassName: '',
|
||||
labelStyle: {},
|
||||
multiple: false,
|
||||
noErrorsDescription: false,
|
||||
style: {},
|
||||
value: [],
|
||||
};
|
||||
@ -109,6 +140,10 @@ InputFileWithErrors.defaultProps = {
|
||||
InputFileWithErrors.propTypes = {
|
||||
className: PropTypes.string,
|
||||
customBootstrapClass: PropTypes.string,
|
||||
didCheckErrors: PropTypes.bool,
|
||||
errors: PropTypes.array,
|
||||
errorsClassName: PropTypes.string,
|
||||
errorsStyle: PropTypes.object,
|
||||
inputDescription: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.func,
|
||||
@ -131,6 +166,7 @@ InputFileWithErrors.propTypes = {
|
||||
labelStyle: PropTypes.object,
|
||||
multiple: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
noErrorsDescription: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
value: PropTypes.oneOfType([
|
||||
|
@ -178,7 +178,8 @@ module.exports = {
|
||||
if (params.plugin === 'upload' && relation.model || relation.collection === 'file') {
|
||||
params = {
|
||||
type: 'media',
|
||||
multiple: params.collection ? true : false
|
||||
multiple: params.collection ? true : false,
|
||||
required: params.required
|
||||
};
|
||||
} else {
|
||||
params = _.omit(params, ['collection', 'model', 'via']);
|
||||
@ -288,7 +289,8 @@ module.exports = {
|
||||
attrs[attribute.name] = {
|
||||
[attribute.params.multiple ? 'collection' : 'model']: 'file',
|
||||
via,
|
||||
plugin: 'upload'
|
||||
plugin: 'upload',
|
||||
required: attribute.params.required === true ? true : false
|
||||
};
|
||||
}
|
||||
} else if (_.has(attribute, 'params.target')) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user