Skip to content

Commit 7ef9462

Browse files
WIP - HOC validation
1 parent 81607fc commit 7ef9462

File tree

6 files changed

+210
-206
lines changed

6 files changed

+210
-206
lines changed

src/examples/Example.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Step2 from './Step2'
77
import Step3 from './Step3'
88
import Step4 from './Step4'
99
import Step5 from './Step5'
10-
import StepValidationTest from './StepValidationTest'
10+
import Step6 from './Step6'
1111

1212
export default class Example extends Component {
1313
constructor(props) {
@@ -39,12 +39,12 @@ export default class Example extends Component {
3939
render() {
4040
const steps =
4141
[
42-
{name: 'StepValidationTest', component: <StepValidationTest getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
4342
{name: 'Step1', component: <Step1 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
4443
{name: 'Step2', component: <Step2 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
4544
{name: 'Step3', component: <Step3 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
4645
{name: 'step4', component: <Step4 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
47-
{name: 'Step5', component: <Step5 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />}
46+
{name: 'Step5', component: <Step5 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />},
47+
{name: 'Step6', component: <Step6 getStore={() => (this.getStore())} updateStore={(u) => {this.updateStore(u)}} />}
4848
]
4949

5050
return (
@@ -53,7 +53,10 @@ export default class Example extends Component {
5353
<StepZilla
5454
steps={steps}
5555
preventEnterSubmission={true}
56-
nextTextOnFinalActionStep={"Save"} />
56+
nextTextOnFinalActionStep={"Save"}
57+
hocValidationAppliedTo={[3]}
58+
hocValidationPredicate={(compRef) => (compRef.isValid()) }
59+
/>
5760
</div>
5861
</div>
5962
)

src/examples/Step4.js

Lines changed: 78 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,94 @@
11
'use strict';
22

33
import React, { Component, PropTypes } from 'react';
4-
import Promise from 'promise';
4+
import validation from 'react-validation-mixin';
5+
import strategy from 'joi-validation-strategy';
6+
import Joi from 'joi';
57

6-
export default class Step4 extends Component {
7-
constructor(props) {
8-
super(props);
8+
class Step4 extends Component {
9+
constructor(props) {
10+
super(props);
911

10-
this.state = {
11-
saving: false
12-
};
13-
14-
this.isValidated = this._isValidated.bind(this); // provide a public isValidated() method. Here its bound to a private _isValidated method
15-
}
16-
17-
componentDidMount() {}
12+
this.state = {
13+
email: ''
14+
};
1815

19-
componentWillUnmount() {}
16+
this.validatorTypes = {
17+
email: Joi.string().email().required()
18+
};
2019

21-
// This review screen had the 'Save' button, on clicking this is called
22-
_isValidated() {
23-
// typically this method needs to return true or false (to indicate if the local forms are validated, so StepZilla can move to the next step),
24-
// but in this example we simulate an ajax request which is async. In the case of async validation or server saving etc. return a Promise and StepZilla will wait
25-
// ... for the resolve() to work out if we can move to the next step
26-
// So here are the rules:
27-
// ~~~~~~~~~~~~~~~~~~~~~~~~
28-
// SYNC action (e.g. local JS form validation).. if you return:
29-
// true/undefined: validation has passed. Move to next step.
30-
// false: validation failed. Stay on current step
31-
// ~~~~~~~~~~~~~~~~~~~~~~~~
32-
// ASYNC return (server side validation or saving data to server etc).. you need to return a Promise which can resolve like so:
33-
// resolve(): validation/save has passed. Move to next step.
34-
// reject(): validation/save failed. Stay on current step
20+
this.getValidatorData = this.getValidatorData.bind(this);
21+
this.renderHelpText = this.renderHelpText.bind(this);
22+
}
3523

36-
this.setState({
37-
saving: true
38-
});
39-
40-
return new Promise((resolve, reject) => {
41-
setTimeout(() => {
42-
this.setState({
43-
saving: true
44-
});
24+
getValidatorData() {
25+
return {
26+
email: this.refs.email.value,
27+
}
28+
};
4529

46-
this.props.updateStore({savedToCloud: true}); // Update store here (this is just an example, in reality you will do it via redux or flux)
30+
onChange(e) {
31+
let newState = {};
32+
newState[e.target.name] = e.target.value;
33+
this.setState(newState);
34+
}
4735

48-
// call resolve() to indicate that server validation or other aync method was a success.
49-
// ... only then will it move to the next step. reject() will indicate a fail
50-
resolve();
51-
// reject(); // or reject
52-
}, 5000);
53-
});
54-
}
36+
renderHelpText(message, id) {
37+
return (<div className="val-err-tooltip" key={id}><span>{message}</span></div>);
38+
};
5539

56-
jumpToStep(toStep) {
57-
// We can explicitly move to a step (we -1 as its a zero based index)
58-
this.props.jumpToStep(toStep-1); // The StepZilla library injects this jumpToStep utility into each component
59-
}
40+
render() {
41+
// explicit class assigning based on validation
42+
let notValidClasses = {};
43+
notValidClasses.emailCls = this.props.isValid('email') ?
44+
'no-error col-md-8' : 'has-error col-md-8';
6045

61-
render() {
62-
const savingCls = this.state.saving ? 'saving col-md-12 show' : 'saving col-md-12 hide';
46+
return (
47+
<div className="step step4">
48+
<div className="row">
49+
<form id="Form" className="form-horizontal">
50+
<div className="form-group">
51+
<label className="col-md-12 control-label">
52+
<h1>Step 4: Enter your emergency contacts details:</h1>
53+
</label>
54+
</div>
55+
<div className="form-group col-md-12">
56+
<label className="control-label col-md-4">
57+
Email
58+
</label>
59+
<div className={notValidClasses.emailCls}>
60+
<input
61+
ref="email"
62+
autoComplete="off"
63+
type="email"
64+
placeholder="[email protected]"
65+
className="form-control"
66+
name="email"
67+
value={this.state.email}
68+
required
69+
onBlur={this.props.handleValidation('email')}
70+
onChange={this.onChange.bind(this)}
71+
/>
6372

64-
return (
65-
<div className="step step4 review">
66-
<div className="row">
67-
<form id="Form" className="form-horizontal">
68-
<div className="form-group">
69-
<label className="col-md-12 control-label">
70-
<h1>Step 4: Review your Details and 'Save'</h1>
71-
</label>
72-
</div>
73-
<div className="form-group">
74-
<div className="col-md-12 control-label">
75-
<div className="col-md-12 txt">
76-
<div className="col-md-4">
77-
Gender
78-
</div>
79-
<div className="col-md-4">
80-
{this.props.getStore().gender}
81-
</div>
82-
</div>
83-
<div className="col-md-12 txt">
84-
<div className="col-md-4">
85-
Email
86-
</div>
87-
<div className="col-md-4">
88-
{this.props.getStore().email}
89-
</div>
90-
</div>
91-
<div className="col-md-12 eg-jump-lnk">
92-
<a href="#" onClick={() => this.jumpToStep(1)}>e.g. showing how we use the jumpToStep method helper method to jump back to step 1</a>
73+
{this.props.getValidationMessages('email').map(this.renderHelpText)}
74+
</div>
75+
</div>
76+
</form>
9377
</div>
94-
<h2 className={savingCls}>Saving to Cloud, pls wait (by the way, we are using a Promise to do this :)...</h2>
95-
</div>
9678
</div>
97-
</form>
98-
</div>
99-
</div>
100-
)
101-
}
79+
)
80+
}
10281
}
82+
83+
Step4.propTypes = {
84+
errors: PropTypes.object,
85+
validate: PropTypes.func,
86+
isValid: PropTypes.func,
87+
handleValidation: PropTypes.func,
88+
getValidationMessages: PropTypes.func,
89+
clearValidations: PropTypes.func,
90+
getStore: PropTypes.func,
91+
updateStore: PropTypes.func,
92+
};
93+
94+
export default validation(strategy)(Step4);

src/examples/Step5.js

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,99 @@
11
'use strict';
22

33
import React, { Component, PropTypes } from 'react';
4+
import Promise from 'promise';
45

56
export default class Step5 extends Component {
67
constructor(props) {
78
super(props);
89

910
this.state = {
10-
savedToCloud: props.getStore().savedToCloud
11+
saving: false
1112
};
13+
14+
this.isValidated = this._isValidated.bind(this); // provide a public isValidated() method. Here its bound to a private _isValidated method
1215
}
1316

1417
componentDidMount() {}
1518

1619
componentWillUnmount() {}
1720

18-
// not required as this component has no forms or user entry
19-
// _isValidated() {}
21+
// This review screen had the 'Save' button, on clicking this is called
22+
_isValidated() {
23+
// typically this method needs to return true or false (to indicate if the local forms are validated, so StepZilla can move to the next step),
24+
// but in this example we simulate an ajax request which is async. In the case of async validation or server saving etc. return a Promise and StepZilla will wait
25+
// ... for the resolve() to work out if we can move to the next step
26+
// So here are the rules:
27+
// ~~~~~~~~~~~~~~~~~~~~~~~~
28+
// SYNC action (e.g. local JS form validation).. if you return:
29+
// true/undefined: validation has passed. Move to next step.
30+
// false: validation failed. Stay on current step
31+
// ~~~~~~~~~~~~~~~~~~~~~~~~
32+
// ASYNC return (server side validation or saving data to server etc).. you need to return a Promise which can resolve like so:
33+
// resolve(): validation/save has passed. Move to next step.
34+
// reject(): validation/save failed. Stay on current step
35+
36+
this.setState({
37+
saving: true
38+
});
39+
40+
return new Promise((resolve, reject) => {
41+
setTimeout(() => {
42+
this.setState({
43+
saving: true
44+
});
45+
46+
this.props.updateStore({savedToCloud: true}); // Update store here (this is just an example, in reality you will do it via redux or flux)
47+
48+
// call resolve() to indicate that server validation or other aync method was a success.
49+
// ... only then will it move to the next step. reject() will indicate a fail
50+
resolve();
51+
// reject(); // or reject
52+
}, 5000);
53+
});
54+
}
55+
56+
jumpToStep(toStep) {
57+
// We can explicitly move to a step (we -1 as its a zero based index)
58+
this.props.jumpToStep(toStep-1); // The StepZilla library injects this jumpToStep utility into each component
59+
}
2060

2161
render() {
62+
const savingCls = this.state.saving ? 'saving col-md-12 show' : 'saving col-md-12 hide';
63+
2264
return (
23-
<div className="step step5">
65+
<div className="step step5 review">
2466
<div className="row">
2567
<form id="Form" className="form-horizontal">
2668
<div className="form-group">
2769
<label className="col-md-12 control-label">
28-
{
29-
(this.state.savedToCloud)
30-
?
31-
<div>
32-
<h1>Thanks!</h1>
33-
<h2>Data was successfully saved to cloud...</h2>
34-
</div>
35-
:
36-
<h1>You have updated data, go <a onClick={() => {this.props.jumpToStep(3)}}>back</a> and Save again!</h1>
37-
}
70+
<h1>Step 4: Review your Details and 'Save'</h1>
3871
</label>
72+
</div>
73+
<div className="form-group">
74+
<div className="col-md-12 control-label">
75+
<div className="col-md-12 txt">
76+
<div className="col-md-4">
77+
Gender
78+
</div>
79+
<div className="col-md-4">
80+
{this.props.getStore().gender}
81+
</div>
82+
</div>
83+
<div className="col-md-12 txt">
84+
<div className="col-md-4">
85+
Email
86+
</div>
87+
<div className="col-md-4">
88+
{this.props.getStore().email}
89+
</div>
90+
</div>
91+
<div className="col-md-12 eg-jump-lnk">
92+
<a href="#" onClick={() => this.jumpToStep(1)}>e.g. showing how we use the jumpToStep method helper method to jump back to step 1</a>
93+
</div>
94+
<h2 className={savingCls}>Saving to Cloud, pls wait (by the way, we are using a Promise to do this :)...</h2>
3995
</div>
96+
</div>
4097
</form>
4198
</div>
4299
</div>

src/examples/Step6.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
import React, { Component, PropTypes } from 'react';
4+
5+
export default class Step6 extends Component {
6+
constructor(props) {
7+
super(props);
8+
9+
this.state = {
10+
savedToCloud: props.getStore().savedToCloud
11+
};
12+
}
13+
14+
componentDidMount() {}
15+
16+
componentWillUnmount() {}
17+
18+
// not required as this component has no forms or user entry
19+
// _isValidated() {}
20+
21+
render() {
22+
return (
23+
<div className="step step6">
24+
<div className="row">
25+
<form id="Form" className="form-horizontal">
26+
<div className="form-group">
27+
<label className="col-md-12 control-label">
28+
{
29+
(this.state.savedToCloud)
30+
?
31+
<div>
32+
<h1>Thanks!</h1>
33+
<h2>Data was successfully saved to cloud...</h2>
34+
</div>
35+
:
36+
<h1>You have updated data, go <a onClick={() => {this.props.jumpToStep(3)}}>back</a> and Save again!</h1>
37+
}
38+
</label>
39+
</div>
40+
</form>
41+
</div>
42+
</div>
43+
)
44+
}
45+
}

0 commit comments

Comments
 (0)