Skip to content
This repository was archived by the owner on Feb 29, 2024. It is now read-only.

Commit 463b388

Browse files
author
Dominik Sumer
committed
implemented warnings (inputs which are valid but have validationmessages), refactored namings
1 parent 7aa4da9 commit 463b388

File tree

9 files changed

+119
-51
lines changed

9 files changed

+119
-51
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.iml
12
.idea/
23
lib/
34
coverage/

README.md

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Take a look at the <a href="https://cat-react.github.io/form/">examples</a> to f
2020
isRequired: true,
2121
equalsField: 'email'
2222
}}
23-
validationErrors={{
23+
messages={{
2424
isRequired: 'Please confirm your email address.',
2525
equalsField: 'The email addresses do not match each other.'
2626
}}/>
@@ -39,37 +39,52 @@ export default class BasicInput extends React.Component {
3939
this.props.setValue(event.target.value);
4040
}
4141

42-
renderErrors() {
43-
let errorMessages = [];
42+
getClassName() {
43+
let className = 'form-control';
44+
if (!this.props.isPristine()) {
45+
if (this.props.isValid()) {
46+
const isWarning = this.props.getMessages().length > 0;
47+
if (isWarning) {
48+
className += ' warning';
49+
}
50+
} else {
51+
className += ' error';
52+
}
53+
}
54+
return className;
55+
}
56+
57+
renderMessages() {
58+
let messages = [];
4459
if (!this.props.isPristine()) {
45-
errorMessages = this.props.getErrorMessages();
60+
messages = this.props.getMessages();
4661
}
4762

48-
if (!errorMessages || errorMessages.length <= 0) {
63+
if (!messages || messages.length <= 0) {
4964
return null;
5065
}
5166

52-
return <ul className="errorText">{errorMessages.map((message, i) => <li key={i}>{message}</li>)}</ul>;
67+
let className = 'errorText';
68+
if (this.props.isValid()) {
69+
className = 'warningText';
70+
}
71+
72+
return <ul className={className}>{messages.map((message, i) => <li key={i}>{message}</li>)}</ul>;
5373
}
5474

5575
render() {
56-
let className = 'form-control';
57-
if (!this.props.isPristine()) {
58-
className += this.props.isValid() ? '' : ' error';
59-
}
60-
6176
return (
6277
<div className="form-group">
6378
<label htmlFor={this.props.name}>{this.props.label} {this.props.isRequired() ? '*' : null}</label>
6479
<input type={this.props.type}
65-
className={className}
80+
className={this.getClassName()}
6681
id={this.props.name}
6782
aria-describedby={this.props.name}
6883
placeholder={this.props.placeholder}
6984
value={this.props.getValue()}
7085
onChange={this.onChange.bind(this)}
71-
onBlur={this.props.touch} />
72-
{this.renderErrors()}
86+
onBlur={this.props.touch}/>
87+
{this.renderMessages()}
7388
</div>
7489
);
7590
}

examples/Home/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
export default class App extends React.Component {
3+
export default class extends React.Component {
44
render() {
55
return (
66
<div className="jumbotron">

examples/Login/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Form from '@cat-react/form/Form';
33
import BasicInput from '../components/BasicInput';
44
import autoBind from 'auto-bind';
55

6-
export default class App extends React.Component {
6+
export default class extends React.Component {
77
constructor(props) {
88
super(props);
99

@@ -49,7 +49,7 @@ export default class App extends React.Component {
4949
type="email"
5050
value=""
5151
validations={{isRequired: true, isEmail: true}}
52-
validationErrors={{
52+
messages={{
5353
isEmail: 'Enter a valid email address.'
5454
}}
5555
placeholder="Enter email"/>
@@ -58,7 +58,7 @@ export default class App extends React.Component {
5858
type="password"
5959
value=""
6060
validations={{isRequired: true, minLength: 3}}
61-
validationErrors={{
61+
messages={{
6262
isRequired: 'Enter your password.',
6363
minLength: 'A password must contain minimum 3 characters.'
6464
}}

examples/Registration/index.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Form from '@cat-react/form/Form';
33
import BasicInput from '../components/BasicInput';
44
import autoBind from 'auto-bind';
55

6-
export default class App extends React.Component {
6+
export default class extends React.Component {
77
constructor(props) {
88
super(props);
99

@@ -57,7 +57,7 @@ export default class App extends React.Component {
5757
type="email"
5858
value=""
5959
validations={{isRequired: true, isEmail: true}}
60-
validationErrors={{
60+
messages={{
6161
isRequired: 'You have to enter your email address.',
6262
isEmail: 'Please enter a valid email address.'
6363
}}
@@ -67,7 +67,7 @@ export default class App extends React.Component {
6767
type="email"
6868
value=""
6969
validations={{isRequired: true, equalsField: 'email'}}
70-
validationErrors={{
70+
messages={{
7171
isRequired: 'Confirm your email address.',
7272
equalsField: 'The email addresses are not matching each other.'
7373
}}
@@ -77,10 +77,20 @@ export default class App extends React.Component {
7777
type="text"
7878
value=""
7979
validations={{minLength: 3}}
80-
validationErrors={{
80+
messages={{
8181
minLength: 'Your First Name has to be minimum 3 characters long.'
8282
}}
8383
placeholder="Enter your First Name"/>
84+
<BasicInput label="Last Name"
85+
name="last_name"
86+
type="text"
87+
value=""
88+
validations={{isRequired: true}}
89+
warnings={['isRequired']}
90+
messages={{
91+
isRequired: 'We appreciate you to submit your last name.'
92+
}}
93+
placeholder="Enter your Last Name"/>
8494
<button type="submit" className="btn btn-primary">Submit</button>
8595
</Form>
8696
);

examples/components/BasicInput.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,52 @@ export default class BasicInput extends React.Component {
77
this.props.setValue(event.target.value);
88
}
99

10-
renderErrors() {
11-
let errorMessages = [];
10+
getClassName() {
11+
let className = 'form-control';
12+
if (!this.props.isPristine()) {
13+
if (this.props.isValid()) {
14+
const isWarning = this.props.getMessages().length > 0;
15+
if (isWarning) {
16+
className += ' warning';
17+
}
18+
} else {
19+
className += ' error';
20+
}
21+
}
22+
return className;
23+
}
24+
25+
renderMessages() {
26+
let messages = [];
1227
if (!this.props.isPristine()) {
13-
errorMessages = this.props.getErrorMessages();
28+
messages = this.props.getMessages();
1429
}
1530

16-
if (!errorMessages || errorMessages.length <= 0) {
31+
if (!messages || messages.length <= 0) {
1732
return null;
1833
}
1934

20-
return <ul className="errorText">{errorMessages.map((message, i) => <li key={i}>{message}</li>)}</ul>;
35+
let className = 'errorText';
36+
if (this.props.isValid()) {
37+
className = 'warningText';
38+
}
39+
40+
return <ul className={className}>{messages.map((message, i) => <li key={i}>{message}</li>)}</ul>;
2141
}
2242

2343
render() {
24-
let className = 'form-control';
25-
if (!this.props.isPristine()) {
26-
className += this.props.isValid() ? '' : ' error';
27-
}
28-
2944
return (
3045
<div className="form-group">
3146
<label htmlFor={this.props.name}>{this.props.label} {this.props.isRequired() ? '*' : null}</label>
3247
<input type={this.props.type}
33-
className={className}
48+
className={this.getClassName()}
3449
id={this.props.name}
3550
aria-describedby={this.props.name}
3651
placeholder={this.props.placeholder}
3752
value={this.props.getValue()}
3853
onChange={this.onChange.bind(this)}
39-
onBlur={this.props.touch} />
40-
{this.renderErrors()}
54+
onBlur={this.props.touch}/>
55+
{this.renderMessages()}
4156
</div>
4257
);
4358
}

examples/index.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@
1717
.errorText {
1818
color: darkred;
1919
}
20+
.warning {
21+
border-color: orange !important;
22+
}
23+
.warningText {
24+
color: orangered;
25+
}

src/Input.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function (WrappedComponent) {
2020
value: props.value,
2121
pristine: true,
2222
valid: false,
23-
errors: []
23+
messages: []
2424
};
2525

2626
autoBind(this);
@@ -74,7 +74,7 @@ export default function (WrappedComponent) {
7474
}
7575

7676
isValid() {
77-
return this.state.valid && this.state.errors.length < 1;
77+
return this.state.valid;
7878
}
7979

8080
getValue() {
@@ -94,8 +94,8 @@ export default function (WrappedComponent) {
9494
});
9595
}
9696

97-
getErrorMessages() {
98-
return this.state.errors;
97+
getMessages() {
98+
return this.state.messages;
9999
}
100100

101101
touch() {
@@ -113,27 +113,30 @@ export default function (WrappedComponent) {
113113
}
114114

115115
async runValidationRules(resolve) {
116-
let errors = [];
116+
let messages = [];
117117

118118
let allValid = true;
119119
for (let ruleName in this.props.validations) {
120120
const ruleConditions = this.props.validations[ruleName];
121121
if (ruleConditions) { // only execute validations if the ruleConditions are valid
122122
const valid = await this.runValidationRule(ruleName);
123123
if (!valid) {
124-
allValid = false;
124+
const isWarning = this.props.warnings.indexOf(ruleName) > -1;
125+
if (!isWarning) {
126+
allValid = false;
127+
}
125128

126-
if (this.props.validationErrors && this.props.validationErrors[ruleName]) {
129+
if (this.props.messages && this.props.messages[ruleName]) {
127130
// TODO: add support for arguments, maybe even different errormessages per validator?
128-
errors.push(this.props.validationErrors[ruleName]);
131+
messages.push(this.props.messages[ruleName]);
129132
}
130133
}
131134
}
132135
}
133136

134137
this.setState({
135138
valid: allValid,
136-
errors: errors
139+
messages: messages
137140
}, () => {
138141
resolve(allValid);
139142
});
@@ -159,7 +162,7 @@ export default function (WrappedComponent) {
159162
isValid: this.isValid,
160163
getValue: this.getValue,
161164
setValue: this.setValue,
162-
getErrorMessages: this.getErrorMessages,
165+
getMessages: this.getMessages,
163166
touch: this.touch,
164167
...this.props
165168
};
@@ -172,10 +175,12 @@ export default function (WrappedComponent) {
172175
Input.propTypes = {
173176
name: PropTypes.string.isRequired,
174177
validations: PropTypes.object,
175-
validationErrors: PropTypes.object,
178+
warnings: PropTypes.arrayOf(PropTypes.string),
179+
messages: PropTypes.object,
176180
dependencies: PropTypes.arrayOf(PropTypes.string)
177181
};
178182
Input.defaultProps = {
183+
warnings: [],
179184
dependencies: []
180185
};
181186
Input.contextTypes = {

tests/Input.spec.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('Input', () => {
4141
expect(instance.isPristine()).toBe(true);
4242
expect(instance.isValid()).toBe(false);
4343
expect(instance.getValue()).toBe('myValue');
44-
expect(instance.getErrorMessages().length).toBe(0);
44+
expect(instance.getMessages().length).toBe(0);
4545

4646
wrapper.setProps({validations: {isRequired: true}});
4747
expect(instance.isRequired()).toBe(true);
@@ -110,20 +110,34 @@ describe('Input', () => {
110110
}}/>, formContext);
111111

112112
await expect(wrapper.instance().validate()).resolves.toBe(false);
113-
expect(wrapper.instance().getErrorMessages()).toEqual([]);
113+
expect(wrapper.instance().getMessages()).toEqual([]);
114114
});
115115

116-
it('should invalidate the inputand show the error messages', async () => {
116+
it('should invalidate the input and show the error messages', async () => {
117117
let wrapper = shallow(<CustomInput name="email" dependencies={['email2']}
118118
validations={{
119119
isRequired: true
120120
}}
121-
validationErrors={{
121+
messages={{
122122
isRequired: 'the input is required'
123123
}}/>, formContext);
124124

125125
await expect(wrapper.instance().validate()).resolves.toBe(false);
126-
expect(wrapper.instance().getErrorMessages()).toEqual(['the input is required']);
126+
expect(wrapper.instance().getMessages()).toEqual(['the input is required']);
127+
});
128+
129+
it('should validate the input but show the warning messages', async () => {
130+
let wrapper = shallow(<CustomInput name="email" dependencies={['email2']}
131+
validations={{
132+
isRequired: true
133+
}}
134+
messages={{
135+
isRequired: 'the input is required'
136+
}}
137+
warnings={['isRequired']}/>, formContext);
138+
139+
await expect(wrapper.instance().validate()).resolves.toBe(true);
140+
expect(wrapper.instance().getMessages()).toEqual(['the input is required']);
127141
});
128142

129143
it('should validate the input with a custom validator', async () => {
@@ -135,5 +149,7 @@ describe('Input', () => {
135149
}}/>, formContext);
136150

137151
await expect(wrapper.instance().validate()).resolves.toBe(true);
152+
wrapper.instance().setValue('asd');
153+
await expect(wrapper.instance().validate()).resolves.toBe(false);
138154
});
139155
});

0 commit comments

Comments
 (0)