-
Notifications
You must be signed in to change notification settings - Fork 0
/
FormContainer.js
164 lines (155 loc) · 5.39 KB
/
FormContainer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import React, { Component, PropTypes } from 'react';
import Formous from 'formous';
import objectAssign from 'object-assign';
import UserDetailSection from '../components/UserDetailSection';
import CommentSection from '../components/CommentSection';
import FormWrapper from '../components/FormWrapper';
import formSubmitHelpers from '../utils/FormHelpers';
const ErrorText = (props) => {
return (
<div style={{ color: '#f00' }}>
{props.errorText}
</div>
);
};
// Stateful Container Component for handling the data and behaviour of our form
class FormContainer extends Component {
constructor(props) {
super(props);
// Set our initial state
this.state = {
step: 1,
fieldValues: {
firstname: '',
surname: '',
email: '',
telephone: '',
gender: 'male',
comments: '',
morecomments: '',
dateofbirth: '',
},
isStepOne: true,
isStepTwo: false,
isStepThree: false,
};
// Since we are using es6 class syntax to create our component instead of
// React's createCLass method we need to explicity bind our methods to our class object
this.handleFieldChange = this.handleFieldChange.bind(this);
this.handleNextStep = this.handleNextStep.bind(this);
this.showStep = this.showStep.bind(this);
this.handleSubmitForm = this.handleSubmitForm.bind(this);
}
handleFieldChange(event) {
const nextState = {};
// RadioGroup component passes event.target.value to our event handler instead of event
// So we make a check for our possible event values from our Radio group fields
if (event === 'male' || event === 'female') {
const eventValue = event;
nextState.gender = eventValue;
} else {
// Gets the name and value attribute of target element (e.g. input) and creates a key value
// pair of both attributes
// This also allows us to have one event handler for every input change event
nextState[event.target.name] = event.target.value;
}
// To avoid mutating state directly we use objectAssign to make a
// shallow copy of our components state with the next state merged in.
// We then use setState to make internal state changes.
const updatedFieldValues = objectAssign({}, this.state.fieldValues, nextState);
this.setState({ fieldValues: updatedFieldValues });
}
handleNextStep() {
// if our current step is less than 3 we want to increment our step with setState
if (this.state.step < 3) {
this.setState({ step: this.state.step + 1 });
}
this.showStep();
}
showStep() {
// We check if this.state.step equals one/two because this.setState does not immediateley mutate
// this.state but creates a pending transition to that next state, so accessing this.state.step
// can potentially return the existing value
if (this.state.step === 1) {
this.setState({ isStepOne: false, isStepTwo: true });
} else if (this.state.step === 2) {
this.setState({ isStepThree: true, isStepTwo: false });
}
}
// handles submission of our form
handleSubmitForm(formStatus) {
event.preventDefault();
if (!formStatus.touched) {
alert('Please fill out the form.');
return;
}
// We post our form data and if we get an ok response then we change route to
// Thank you page
formSubmitHelpers.postFormData(this.state.fieldValues)
.then((response) => {
if (response.result === 'ok') {
// We grab router from our context type and call push method to change routes
// to Thank You page/route
this.context.router.push({
pathname: '/thankYou',
});
}
});
}
// Our render method returns a tree of components, passing properties down to those components
render() {
const {
fields: { },
formSubmit,
} = this.props;
return (
<FormWrapper>
<form onSubmit={formSubmit(this.handleSubmitForm)}>
<UserDetailSection
headerTitle="Step 1: Your details"
fieldValues={this.state.fieldValues}
onFieldChange={this.handleFieldChange}
onNextStep={this.handleNextStep}
show={this.state.isStepOne}
/>
<CommentSection
currentStep={this.state.step}
headerTitle="Step 2: More comments"
name="comments"
comments={this.state.fieldValues.comments}
onFieldChange={this.handleFieldChange}
onNextStep={this.handleNextStep}
show={this.state.isStepTwo}
/>
<CommentSection
currentStep={this.state.step}
headerTitle="Step 3: Final comments"
name="morecomments"
comments={this.state.fieldValues.morecomments}
onFieldChange={this.handleFieldChange}
show={this.state.isStepThree}
/>
</form>
</FormWrapper>
);
}
}
const fields = {
dateofbirth: {
tests: [
{
critical: true,
failProps: {
errorText: 'Age should be a number.',
},
test(value) {
return /^\d*$/.test(value);
},
},
],
},
};
// We set router as a property of our component's contextType so we don't have to pass router as
// props down to our Form Container Component.
FormContainer.contextTypes = { router: PropTypes.object.isRequired };
export default Formous(fields)(FormContainer);