diff --git a/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js b/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js index 8a457abaa4..e1c93ae06e 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js @@ -61,7 +61,10 @@ InputNumber.propTypes = { placeholder: PropTypes.string, style: PropTypes.object, tabIndex: PropTypes.string, - value: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]).isRequired, }; export default InputNumber; diff --git a/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js new file mode 100644 index 0000000000..a677f0d74c --- /dev/null +++ b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js @@ -0,0 +1,164 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { includes, isEmpty, isFunction, mapKeys, reject } from 'lodash'; +import cn from 'classnames'; + +// Design +import Label from 'components/Label'; +import InputDescription from 'components/InputDescription'; +import InputErrors from 'components/InputErrors'; +import InputNumber from 'components/InputNumber'; + +import styles from './styles.scss'; + +class InputNumberWithErrors extends React.Component { // eslint-disable-line react/prefer-stateless-function + state = { errors: [], hasInitialValue: false }; + + componentDidMount() { + const { value, errors } = this.props; + + // Prevent the input from displaying an error when the user enters and leaves without filling it + if (value && !isEmpty(value)) { + this.setState({ hasInitialValue: true }); + } + + // Display input error if it already has some + if (!isEmpty(errors)) { + this.setState({ errors }); + } + } + + componentWillReceiveProps(nextProps) { + // 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 }); + } + } + + /** + * Set the errors depending on the validations given to the input + * @param {Object} target + */ + handleBlur = ({ target }) => { + // Prevent from displaying error if the input is initially isEmpty + if (!isEmpty(target.value) || this.state.hasInitialValue) { + const errors = this.validate(target.value); + this.setState({ errors, hasInitialValue: true }); + } + } + + render() { + const { autoFocus, errorsClassName, errorsStyle, inputClassName, inputStyle, name, onChange, onFocus, placeholder, value } = this.props; + const handleBlur = isFunction(this.props.onBlur) ? this.props.onBlur : this.handleBlur; + + return ( +
+
+ ); + } + + validate = (value) => { + const requiredError = { id: 'components.Input.error.validation.required' }; + let errors = []; + + mapKeys(this.props.validations, (validationValue, validationKey) => { + switch (validationKey) { + case 'max': { + if (parseInt(value, 10) > validationValue) { + errors.push({ id: 'components.Input.error.validation.max' }); + } + break; + } + case 'min': { + if (parseInt(value, 10) < validationValue) { + errors.push({ id: 'components.Input.error.validation.min' }); + } + break; + } + case 'required': { + if (value.length === 0) { + console.log('ok'); + errors.push({ id: 'components.Input.error.validation.required' }); + } + break; + } + case 'regex': { + if (!new RegExp(validationValue).test(value)) { + errors.push({ id: 'components.Input.error.validation.regex' }); + } + break; + } + default: + errors = []; + } + }); + + if (includes(errors, requiredError)) { + errors = reject(errors, (error) => error !== requiredError); + } + + return errors; + } +} + +InputNumberWithErrors.defaultProps = { + customBootstrapClass: false, + didCheckErrors: false, + onBlur: false, + onFocus: () => {}, + errors: [], + errorsClassName: '', + errorsStyle: {}, + inputClassName: '', + inputStyle: {}, + placeholder: 'app.utils.placeholder.defaultMessage', + validations: {}, +}; + +InputNumberWithErrors.propTypes = { + customBootstrapClass: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]), + didCheckErrors: PropTypes.bool, + errors: PropTypes.array, + errorsClassName: PropTypes.string, + errorsStyle: PropTypes.object, + inputClassName: PropTypes.string, + inputStyle: PropTypes.object, + onBlur: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.func, + ]), + onChange: PropTypes.func.isRequired, + onFocus: PropTypes.func, + validations: PropTypes.object, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), +}; + +export default InputNumberWithErrors; diff --git a/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/styles.scss b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/styles.scss new file mode 100644 index 0000000000..5d8578e7d5 --- /dev/null +++ b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/styles.scss @@ -0,0 +1,5 @@ +.container { + min-width: 200px; + margin-bottom: 1.5rem; + font-size: 1.3rem; +} diff --git a/packages/strapi-helper-plugin/lib/src/components/InputTextWithErrors/index.js b/packages/strapi-helper-plugin/lib/src/components/InputTextWithErrors/index.js index ccdb9cc6da..035488f249 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputTextWithErrors/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/InputTextWithErrors/index.js @@ -103,6 +103,12 @@ class InputTextWithErrors extends React.Component { // eslint-disable-line react } break; } + case 'regex': { + if (!new RegExp(validationValue).test(value)) { + errors.push({ id: 'components.Input.error.validation.regex' }); + } + break; + } default: errors = []; }