Show form errors

This commit is contained in:
Manuel Bouza
2019-02-13 14:22:50 +01:00
parent eb154be04a
commit 4d77ddb2aa
6 changed files with 48 additions and 12 deletions

View File

@@ -11,6 +11,7 @@
},
"dependencies": {
"axios": "^0.18.0",
"classnames": "^2.2.6",
"date-fns": "^1.30.1",
"lodash": "^4.17.11",
"mobx": "^5.5.0",

View File

@@ -24,6 +24,16 @@
color: #eee;
}
&.has-error {
input, textarea {
border-color: #FB3A2F;
}
}
.form-error {
color: #FB3A2F;
}
.input-group {
position: relative;
display: table;

View File

@@ -147,6 +147,7 @@ class Bubble extends Component {
.createActivity(this.changesetWithDefaults)
.then(() => {
this.close()
this.changeset = {}
this.formErrors = {}
})
.catch(this.handleSubmitError)
@@ -173,6 +174,7 @@ class Bubble extends Component {
<Form
projects={this.projects}
changeset={this.changesetWithDefaults}
errors={this.formErrors}
isLoading={this.isLoading}
onChange={this.handleChange}
onSubmit={this.handleSubmit}

View File

@@ -1,6 +1,7 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import Select from "components/Select"
import cn from "classnames"
class Form extends Component {
static propTypes = {
@@ -10,6 +11,7 @@ class Form extends Component {
task: PropTypes.object,
hours: PropTypes.string
}).isRequired,
errors: PropTypes.object,
projects: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired
@@ -33,29 +35,39 @@ class Form extends Component {
return null
}
const { projects, changeset, onChange, onSubmit } = this.props
const { projects, changeset, errors, onChange, onSubmit } = this.props
const project = Select.findOptionByValue(projects, changeset.assignment_id)
return (
<form onSubmit={onSubmit}>
<div className="form-group">
<div
className={cn("form-group", { "has-error": errors.assignment_id })}
>
<Select
name="assignment_id"
options={projects}
value={changeset.assignment_id}
hasError={!!errors.assignment_id}
onChange={onChange}
/>
{errors.assignment_id ? (
<div className="form-error">{errors.assignment_id.join('; ')}</div>
) : null}
</div>
<div className="form-group">
<div className={cn("form-group", { "has-error": errors.task_id })}>
<Select
name="task_id"
options={project?.tasks || []}
value={changeset.task_id}
onChange={onChange}
hasError={!!errors.task_id}
noOptionsMessage={() => "Zuerst Projekt wählen"}
/>
{errors.task_id ? (
<div className="form-error">{errors.task_id.join('; ')}</div>
) : null}
</div>
<div className="form-group">
<div className={cn("form-group", { "has-error": errors.hours })}>
<input
name="hours"
className="form-control"
@@ -65,8 +77,11 @@ class Form extends Component {
autoComplete="off"
autoFocus
/>
{errors.hours ? (
<div className="form-error">{errors.hours.join('; ')}</div>
) : null}
</div>
<div className="form-group">
<div className={cn("form-group", { "has-error": errors.description })}>
<textarea
name="description"
onChange={onChange}
@@ -74,6 +89,9 @@ class Form extends Component {
placeholder="Beschreibung der Tätigkeit - mind. 3 Zeichen"
rows={4}
/>
{errors.description ? (
<div className="form-error">{errors.description.join('; ')}</div>
) : null}
</div>
<button disabled={!this.isValid()}>Speichern</button>

View File

@@ -12,6 +12,7 @@ import {
flatMap,
pathEq
} from "lodash/fp"
import { trace } from "utils"
const customTheme = theme => ({
...theme,
@@ -23,7 +24,11 @@ const customTheme = theme => ({
}
})
const customStyles = {
const customStyles = props => ({
control: (base, _state) => ({
...base,
borderColor: props.hasError ? "#FB3A2F" : base.borderColor
}),
groupHeading: (base, _state) => ({
...base,
color: "black",
@@ -31,7 +36,7 @@ const customStyles = {
fontWeight: "bold",
fontSize: "100%"
})
}
})
const filterOption = createFilter({
stringify: compose(
@@ -47,10 +52,10 @@ export default class Select extends Component {
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
options: PropTypes.array,
hasError: PropTypes.bool,
onChange: PropTypes.func.isRequired
};
static findOptionByValue = (selectOptions, value) => {
const options = flatMap(
option => (option.options ? option.options : option),
@@ -58,7 +63,7 @@ export default class Select extends Component {
)
return options.find(pathEq("value", value))
}
};
handleChange = option => {
const { name, onChange } = this.props
@@ -67,7 +72,7 @@ export default class Select extends Component {
};
render() {
const { value, ...passThroughProps } = this.props
const { value, hasError, ...passThroughProps } = this.props
return (
<ReactSelect
{...passThroughProps}
@@ -75,7 +80,7 @@ export default class Select extends Component {
onChange={this.handleChange}
filterOption={filterOption}
theme={customTheme}
styles={customStyles}
styles={customStyles(this.props)}
/>
)
}

View File

@@ -1709,7 +1709,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
classnames@^2.2.5:
classnames@^2.2.5, classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==