Skip to content
This repository was archived by the owner on Sep 2, 2023. It is now read-only.

Commit 5c3a2b6

Browse files
authored
Merge pull request #626 from nazaninreihani/profile-validate
Profile validate
2 parents e4fef73 + e9d611b commit 5c3a2b6

File tree

4 files changed

+222
-16
lines changed

4 files changed

+222
-16
lines changed

src/settings/SettingsAddress.js

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import React, { PureComponent } from 'react';
2+
import head from 'lodash.head';
3+
import validate from 'validate.js';
24
import { Legend, Button, InputGroup, ErrorMsg, ServerErrorMsg } from 'binary-components';
35
import { actions } from '../_store';
46
import States from './States';
57
import UpdateNotice from '../containers/UpdateNotice';
68
import * as LiveData from '../_data/LiveData';
9+
import { getConstraints } from './SettingsAddress.validation.config';
710

811
export default class SettingsAddress extends PureComponent {
912

@@ -28,6 +31,14 @@ export default class SettingsAddress extends PureComponent {
2831
address_state: props.address_state,
2932
address_postcode: props.address_postcode,
3033
phone: props.phone,
34+
touched: {
35+
address_line_1: false,
36+
address_line_2: false,
37+
address_city: false,
38+
address_state: false,
39+
address_postcode: false,
40+
phone: false,
41+
},
3142
};
3243
}
3344

@@ -36,15 +47,27 @@ export default class SettingsAddress extends PureComponent {
3647
actions.getStatesForCountry(country_code);
3748
}
3849

39-
onEntryChange = (e: SyntheticEvent) =>
40-
this.setState({ [e.target.id]: e.target.value });
50+
onEntryChange = (e: SyntheticEvent) =>
51+
this.setState({
52+
[e.target.id]: e.target.value,
53+
touched: { ...this.state.touched, [e.target.id]: true },
54+
hasError: false,
55+
}, () => {
56+
this.validateForm();
57+
});
4158

42-
onFormSubmit = (e: SyntheticEvent) => {
43-
e.preventDefault();
59+
validateForm = () => {
60+
this.constraints = getConstraints(this.props, this.state);
4461
this.setState({
45-
validatedOnce: true,
62+
errors: validate(this.state, this.constraints, { format: 'grouped', fullMessages: false, cleanAttributes: false }) || {},
4663
});
47-
if (this.allValid) {
64+
}
65+
66+
onFormSubmit = (e: SyntheticEvent) => {
67+
e.preventDefault();
68+
if (Object.keys(this.state.errors).length > 0) {
69+
this.setState({ hasError: true });
70+
} else {
4871
this.performUpdateSettings();
4972
}
5073
}
@@ -71,63 +94,69 @@ export default class SettingsAddress extends PureComponent {
7194

7295
render() {
7396
const { states } = this.props;
74-
const { validatedOnce, address_line_1, address_line_2, address_city, address_state,
75-
address_postcode, country_code, phone, phoneError, serverError, success } = this.state;
76-
77-
this.allValid = address_line_1 && address_city;
97+
const { address_line_1, address_line_2, address_city, address_state,
98+
address_postcode, country_code, phone, serverError, success, hasError, touched, errors } = this.state;
7899

79100
return (
80101
<form className="settings-address" onSubmit={this.onFormSubmit}>
81102
{serverError && <ServerErrorMsg text={serverError} />}
103+
{hasError && <ErrorMsg text="Please fill the form with valid values" />}
82104
<UpdateNotice text="Address updated" show={success} />
83105
<Legend text="Address" />
84106
<InputGroup
85107
id="address_line_1"
108+
name="address_line_1"
86109
type="text"
87110
label="Address"
88111
value={address_line_1}
89112
onChange={this.onEntryChange}
90113
/>
91-
{validatedOnce && !address_line_1 &&
92-
<ErrorMsg text="This field is required." />
93-
}
114+
{touched.address_line_1 && <ErrorMsg text={head((errors || {}).address_line_1)} />}
94115
<InputGroup
95116
id="address_line_2"
117+
name="address_line_2"
96118
type="text"
97119
label=" "
98120
value={address_line_2}
99121
onChange={this.onEntryChange}
100122
/>
123+
{touched.address_line_2 && <ErrorMsg text={head((errors || {}).address_line_2)} />}
101124
<InputGroup
102125
id="address_city"
126+
name="address_city"
103127
type="text"
104128
label="Town/City"
105129
defaultValue={address_city}
106130
onChange={this.onEntryChange}
107131
/>
132+
{touched.address_city && <ErrorMsg text={head((errors || {}).address_city)} />}
108133
<States
109134
id="address_state"
135+
name="address_state"
110136
country={country_code}
111137
states={states}
112138
onChange={this.onEntryChange}
113139
selected={address_state}
114140
/>
141+
{touched.address_state && <ErrorMsg text={head((errors || {}).address_state)} />}
115142
<InputGroup
116143
id="address_postcode"
144+
name="address_postcode"
117145
type="text"
118146
label="Postal Code / ZIP"
119147
defaultValue={address_postcode}
120148
onChange={this.onEntryChange}
121149
/>
150+
{touched.address_postcode && <ErrorMsg text={head((errors || {}).address_postcode)} />}
122151
<InputGroup
123152
id="phone"
153+
name="phone"
124154
type="tel"
125155
label="Telephone"
126156
defaultValue={phone}
127157
onChange={this.onEntryChange}
128158
/>
129-
{phoneError === 'length' && <ErrorMsg text="You should enter between 6-35 characters." />}
130-
{phoneError === 'allowed' && <ErrorMsg text="Only numbers, space, - are allowed." />}
159+
{touched.phone && <ErrorMsg text={head((errors || {}).phone)} />}
131160
<Button
132161
text="Update"
133162
onClick={this.tryUpdate}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import validate from 'validate.js';
2+
3+
/*eslint-disable */
4+
validate.validators.validateAddress = (value) => {
5+
if(/[`~!#$%^&*)(_=+\[}{\]\\";:\?><|]+/.test(value)) {
6+
return 'Only letters, numbers, space, hyphen, period, and apostrophe are allowed.';
7+
}
8+
}
9+
10+
validate.validators.validateGeneral = (value) => {
11+
if(/[`~!@#$%^&*)(_=+\[}{\]\\\/";:\?><,|\d]+/.test(value)) {
12+
return 'Only letters, space, hyphen, period, and apostrophe are allowed.';
13+
}
14+
}
15+
/*eslint-enable */
16+
17+
export const getConstraints = () => {
18+
const constraints = {
19+
address_line_1: {
20+
presence: {
21+
presence: true,
22+
message: 'This field is required.',
23+
},
24+
validateAddress: true,
25+
},
26+
address_line_2: {
27+
validateAddress: true,
28+
},
29+
address_city: {
30+
presence: {
31+
presence: true,
32+
message: 'This field is required.',
33+
},
34+
validateGeneral: true,
35+
},
36+
address_state: {
37+
presence: {
38+
presence: true,
39+
message: 'This field is required.',
40+
},
41+
},
42+
address_postcode: {
43+
format: {
44+
/*eslint-disable */
45+
pattern: /^([a-zA-Z\d-\s])*$/,
46+
/*eslint-enable */
47+
message: 'Only letters, numbers, space, and hyphen are allowed.',
48+
},
49+
},
50+
phone: {
51+
presence: {
52+
presence: true,
53+
message: 'This field is required.',
54+
},
55+
format: {
56+
/*eslint-disable */
57+
pattern: /^\+?[0-9\s]*$/,
58+
/*eslint-enable */
59+
message: 'Only numbers and spaces are allowed.',
60+
},
61+
},
62+
};
63+
return constraints;
64+
};

src/settings/States.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default class States extends PureComponent {
1717
<fieldset>
1818
{noLabel ? null : <label htmlFor={id}>State/Province</label>}
1919
<select id={id} onChange={onChange} value={selected}>
20-
{states.map((x, i) => (
20+
{states && states.map((x, i) => (
2121
<option key={i} value={x.value}>{x.text}</option>
2222
))}
2323
</select>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import React from 'react';
2+
import SettingsAddress from '../SettingsAddress';
3+
import { mountWithIntl } from 'enzyme-react-intl';
4+
5+
describe('<SettingsSelfExclusion />', () => {
6+
// test max_balance as an input which should be number and being validated (other inputs behave like this too)
7+
it('Component should be rendered', () => {
8+
const wrapper = mountWithIntl(<SettingsAddress />);
9+
10+
expect(wrapper.type()).toBeDefined();
11+
});
12+
13+
it('address line 1 should have required error when input is empty', () => {
14+
const wrapper = mountWithIntl(<SettingsAddress />);
15+
const addressLine1 = wrapper.find('#address_line_1');
16+
addressLine1.node.value = null;
17+
addressLine1.simulate('change');
18+
const errors = wrapper.state('errors');
19+
20+
expect(errors.address_line_1[0]).toEqual('This field is required.');
21+
});
22+
23+
it('address line 1 should be valid when format is valid', () => {
24+
const wrapper = mountWithIntl(<SettingsAddress />);
25+
const addressLine1 = wrapper.find('#address_line_1');
26+
addressLine1.node.value = 'test 123 test';
27+
addressLine1.simulate('change');
28+
const errors = wrapper.state('errors');
29+
30+
expect(errors.address_line_1).toBeUndefined();
31+
});
32+
33+
it('address line 1 should not be valid when format is not valid', () => {
34+
const wrapper = mountWithIntl(<SettingsAddress />);
35+
const addressLine1 = wrapper.find('#address_line_1');
36+
addressLine1.node.value = 'test " test';
37+
addressLine1.simulate('change');
38+
const errors = wrapper.state('errors');
39+
40+
expect(errors.address_line_1[0]).toEqual('Only letters, numbers, space, hyphen, period, and apostrophe are allowed.');
41+
});
42+
43+
it('address line 2 should not have required error when input is empty', () => {
44+
const wrapper = mountWithIntl(<SettingsAddress />);
45+
const addressLine2 = wrapper.find('#address_line_2');
46+
addressLine2.node.value = null;
47+
addressLine2.simulate('change');
48+
const errors = wrapper.state('errors');
49+
50+
expect(errors.address_line_2s).toBeUndefined();
51+
});
52+
53+
it('address city should be valid when format is valid', () => {
54+
const wrapper = mountWithIntl(<SettingsAddress />);
55+
const addressCity = wrapper.find('#address_city');
56+
addressCity.node.value = 'test test';
57+
addressCity.simulate('change');
58+
const errors = wrapper.state('errors');
59+
60+
expect(errors.address_city).toBeUndefined();
61+
});
62+
63+
it('address city should not be valid when format is not valid', () => {
64+
const wrapper = mountWithIntl(<SettingsAddress />);
65+
const addressCity = wrapper.find('#address_city');
66+
addressCity.node.value = 'test " test';
67+
addressCity.simulate('change');
68+
const errors = wrapper.state('errors');
69+
70+
expect(errors.address_city[0]).toEqual('Only letters, space, hyphen, period, and apostrophe are allowed.');
71+
});
72+
73+
it('address city should be valid when format is valid', () => {
74+
const wrapper = mountWithIntl(<SettingsAddress />);
75+
const addressPostCode = wrapper.find('#address_postcode');
76+
addressPostCode.node.value = 'test 123 test';
77+
addressPostCode.simulate('change');
78+
const errors = wrapper.state('errors');
79+
80+
expect(errors.address_postcode).toBeUndefined();
81+
});
82+
83+
it('address city should not be valid when format is not valid', () => {
84+
const wrapper = mountWithIntl(<SettingsAddress />);
85+
const addressPostCode = wrapper.find('#address_postcode');
86+
addressPostCode.node.value = 'test " test';
87+
addressPostCode.simulate('change');
88+
const errors = wrapper.state('errors');
89+
90+
expect(errors.address_postcode[0]).toEqual('Only letters, numbers, space, and hyphen are allowed.');
91+
});
92+
93+
it('phone should be valid when format is valid', () => {
94+
const wrapper = mountWithIntl(<SettingsAddress />);
95+
const phone = wrapper.find('#phone');
96+
phone.node.value = '123 456 789';
97+
phone.simulate('change');
98+
const errors = wrapper.state('errors');
99+
100+
expect(errors.phone).toBeUndefined();
101+
});
102+
103+
it('phone should not be valid when format is not valid', () => {
104+
const wrapper = mountWithIntl(<SettingsAddress />);
105+
const phone = wrapper.find('#phone');
106+
phone.node.value = 'test 123';
107+
phone.simulate('change');
108+
const errors = wrapper.state('errors');
109+
110+
expect(errors.phone[0]).toEqual('Only numbers and spaces are allowed.');
111+
});
112+
113+
});

0 commit comments

Comments
 (0)