Skip to content

Commit c5ee03d

Browse files
authored
Merge branch 'master' into remove-defaultProps
2 parents 2f755d6 + 5653500 commit c5ee03d

File tree

10 files changed

+352
-317
lines changed

10 files changed

+352
-317
lines changed

src/components/form-elements/form/Form.js

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
22
import React, { Component } from 'react';
33
import serialize from 'form-serialize';
44

5+
import { FormContext } from './FormContext';
6+
57
function getFormValidityState(form) {
68
// Turn the form.elements HTMLCollection into Array before reducing
79
return [].slice.call(form.elements).reduce((validityObj, inputEl) => {
@@ -37,13 +39,6 @@ class Form extends Component {
3739
formValidityState: PropTypes.object, // eslint-disable-line react/no-unused-prop-types
3840
};
3941

40-
static childContextTypes = {
41-
form: PropTypes.shape({
42-
registerInput: PropTypes.func.isRequired,
43-
unregisterInput: PropTypes.func.isRequired,
44-
}).isRequired,
45-
};
46-
4742
constructor(props) {
4843
super(props);
4944

@@ -52,15 +47,6 @@ class Form extends Component {
5247
};
5348
}
5449

55-
getChildContext() {
56-
return {
57-
form: {
58-
registerInput: this.registerInput.bind(this),
59-
unregisterInput: this.unregisterInput.bind(this),
60-
},
61-
};
62-
}
63-
6450
componentDidUpdate({ formValidityState: prevFormValidityState }) {
6551
const { formValidityState } = this.props;
6652
const { registeredInputs } = this.state;
@@ -129,9 +115,18 @@ class Form extends Component {
129115
render() {
130116
const { children } = this.props;
131117
return (
132-
<form noValidate onChange={this.onChange} onSubmit={this.onSubmit}>
133-
{children}
134-
</form>
118+
<FormContext.Provider
119+
value={{
120+
form: {
121+
registerInput: this.registerInput,
122+
unregisterInput: this.unregisterInput,
123+
},
124+
}}
125+
>
126+
<form noValidate onChange={this.onChange} onSubmit={this.onSubmit}>
127+
{children}
128+
</form>
129+
</FormContext.Provider>
135130
);
136131
}
137132
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
export const FormContext = React.createContext(null);
5+
6+
FormContext.displayName = 'FormContext';
7+
8+
export const FormContextPropTypes = {
9+
form: PropTypes.shape({
10+
registerInput: PropTypes.func.isRequired,
11+
unregisterInput: PropTypes.func.isRequired,
12+
}),
13+
};
Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import PropTypes from 'prop-types';
22
import React, { Component } from 'react';
33

4+
import { FormContext } from './FormContext';
5+
46
class FormInput extends Component {
57
static propTypes = {
68
children: PropTypes.node.isRequired,
@@ -10,24 +12,19 @@ class FormInput extends Component {
1012
name: PropTypes.string.isRequired,
1113
};
1214

13-
static contextTypes = {
14-
form: PropTypes.shape({
15-
registerInput: PropTypes.func.isRequired,
16-
unregisterInput: PropTypes.func.isRequired,
17-
}),
18-
};
19-
2015
componentDidMount() {
2116
const { name, onValidityStateUpdate } = this.props;
17+
const formContext = this.context;
2218

23-
if (this.context.form) {
24-
this.context.form.registerInput(name, onValidityStateUpdate);
19+
if (formContext && formContext.form) {
20+
formContext.form.registerInput(name, onValidityStateUpdate);
2521
}
2622
}
2723

2824
componentWillUnmount() {
29-
if (this.context.form) {
30-
this.context.form.unregisterInput(this.props.name);
25+
const formContext = this.context;
26+
if (formContext && formContext.form) {
27+
formContext.form.unregisterInput(this.props.name);
3128
}
3229
}
3330

@@ -36,4 +33,6 @@ class FormInput extends Component {
3633
}
3734
}
3835

36+
FormInput.contextType = FormContext;
37+
3938
export default FormInput;

src/components/form-elements/form/__tests__/Form.test.js

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -125,71 +125,76 @@ describe('components/form-elements/form/Form', () => {
125125
});
126126

127127
test('should expose form register/unregister function on the context', () => {
128-
const wrapper = shallow(<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}} />);
128+
const wrapper = mount(
129+
<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}}>
130+
<div />
131+
</Form>,
132+
);
133+
134+
const instance = wrapper.find('Form').instance();
129135

130-
expect(wrapper.instance().getChildContext().form.registerInput).toBeTruthy();
131-
expect(wrapper.instance().getChildContext().form.unregisterInput).toBeTruthy();
136+
expect(instance.registerInput).toBeTruthy();
137+
expect(instance.unregisterInput).toBeTruthy();
132138
});
133139

134140
test('should register an input when registerInput is called', () => {
135-
const wrapper = shallow(<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}} />);
141+
const wrapper = mount(
142+
<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}}>
143+
<div />
144+
</Form>,
145+
);
136146

137147
const inputHandlerSpy = sinon.spy();
138-
wrapper
139-
.instance()
140-
.getChildContext()
141-
.form.registerInput('testinput', inputHandlerSpy);
142-
expect(wrapper.state('registeredInputs').testinput).toBe(inputHandlerSpy);
148+
const instance = wrapper.find('Form').instance();
149+
instance.registerInput('testinput', inputHandlerSpy);
150+
expect(instance.state.registeredInputs.testinput).toBe(inputHandlerSpy);
143151
});
144152

145153
test('should correctly register multiple inputs when registerInput is called', () => {
146-
const wrapper = shallow(<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}} />);
154+
const wrapper = mount(
155+
<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}}>
156+
<div />
157+
</Form>,
158+
);
147159

148160
const inputHandlerSpy = sinon.spy();
149-
wrapper
150-
.instance()
151-
.getChildContext()
152-
.form.registerInput('testinput1', inputHandlerSpy);
153-
wrapper
154-
.instance()
155-
.getChildContext()
156-
.form.registerInput('testinput2', inputHandlerSpy);
157-
expect(wrapper.state('registeredInputs').testinput1).toBe(inputHandlerSpy);
158-
expect(wrapper.state('registeredInputs').testinput2).toBe(inputHandlerSpy);
161+
const instance = wrapper.find('Form').instance();
162+
instance.registerInput('testinput1', inputHandlerSpy);
163+
instance.registerInput('testinput2', inputHandlerSpy);
164+
expect(instance.state.registeredInputs.testinput1).toBe(inputHandlerSpy);
165+
expect(instance.state.registeredInputs.testinput2).toBe(inputHandlerSpy);
159166
});
160167

161168
test('should throw an error if registerInput is called for already registered input', done => {
162-
const wrapper = shallow(<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}} />);
169+
const wrapper = mount(
170+
<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}}>
171+
<div />
172+
</Form>,
173+
);
163174

164-
wrapper
165-
.instance()
166-
.getChildContext()
167-
.form.registerInput('testinput', () => {});
175+
const instance = wrapper.find('Form').instance();
176+
instance.registerInput('testinput', () => {});
168177

169178
try {
170-
wrapper
171-
.instance()
172-
.getChildContext()
173-
.form.registerInput('testinput', () => {});
179+
instance.registerInput('testinput', () => {});
174180
} catch (e) {
175181
expect(e.message).toEqual("Input 'testinput' is already registered.");
176182
done();
177183
}
178184
});
179185

180186
test('should unregister an input when unregisterInput is called', () => {
181-
const wrapper = shallow(<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}} />);
187+
const wrapper = mount(
188+
<Form onInvalidSubmit={() => {}} onValidSubmit={() => {}}>
189+
<div />
190+
</Form>,
191+
);
182192

183193
const inputHandlerSpy = sinon.spy();
184-
wrapper
185-
.instance()
186-
.getChildContext()
187-
.form.registerInput('testinput', inputHandlerSpy);
188-
expect(wrapper.state('registeredInputs').testinput).toBe(inputHandlerSpy);
189-
wrapper
190-
.instance()
191-
.getChildContext()
192-
.form.unregisterInput('testinput', inputHandlerSpy);
193-
expect(wrapper.state('registeredInputs').testinput).toBeFalsy();
194+
const instance = wrapper.find('Form').instance();
195+
instance.registerInput('testinput', inputHandlerSpy);
196+
expect(instance.state.registeredInputs.testinput).toBe(inputHandlerSpy);
197+
instance.unregisterInput('testinput');
198+
expect(instance.state.registeredInputs.testinput).toBeFalsy();
194199
});
195200
});

src/components/form-elements/form/__tests__/FormInput.test.js

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mount } from 'enzyme';
33
import sinon from 'sinon';
44

55
import { FormInput } from '..';
6+
import { FormContext } from '../FormContext';
67

78
const sandbox = sinon.sandbox.create();
89

@@ -12,34 +13,32 @@ describe('components/form-elements/form/FormInput', () => {
1213
});
1314

1415
test('should register itself with the form when form is exposed on context', () => {
15-
const context = {
16-
form: {
17-
registerInput: sandbox.mock().withArgs('forminput'),
18-
unregisterInput: sandbox.mock().never(),
19-
},
16+
const mockForm = {
17+
registerInput: sandbox.mock().withArgs('forminput'),
18+
unregisterInput: sandbox.mock().never(),
2019
};
2120

2221
mount(
23-
<FormInput name="forminput" onValidityStateUpdate={sinon.stub()}>
24-
<input />
25-
</FormInput>,
26-
{ context },
22+
<FormContext.Provider value={{ form: mockForm }}>
23+
<FormInput name="forminput" onValidityStateUpdate={sinon.stub()}>
24+
<input />
25+
</FormInput>
26+
</FormContext.Provider>,
2727
);
2828
});
2929

3030
test('should unregister itself with the form when form is exposed on context and component unmounts', () => {
31-
const context = {
32-
form: {
33-
registerInput: sandbox.mock().withArgs('input'),
34-
unregisterInput: sandbox.mock().withArgs('input'),
35-
},
31+
const mockForm = {
32+
registerInput: sandbox.mock().withArgs('input'),
33+
unregisterInput: sandbox.mock().withArgs('input'),
3634
};
3735

3836
const component = mount(
39-
<FormInput label="label" name="input" onValidityStateUpdate={sinon.stub()} value="">
40-
Children
41-
</FormInput>,
42-
{ context },
37+
<FormContext.Provider value={{ form: mockForm }}>
38+
<FormInput label="label" name="input" onValidityStateUpdate={sinon.stub()} value="">
39+
Children
40+
</FormInput>
41+
</FormContext.Provider>,
4342
);
4443

4544
component.unmount();

src/components/form-elements/text-area/__tests__/TextArea.test.js

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { act } from 'react';
2-
import PropTypes from 'prop-types';
32
import { shallow, mount } from 'enzyme';
43
import sinon from 'sinon';
54

65
import TextArea from '..';
6+
import { FormContext } from '../../form/FormContext';
77

88
const sandbox = sinon.sandbox.create();
99

@@ -179,50 +179,48 @@ describe('components/form-elements/text-area/TextArea', () => {
179179

180180
test('should set validity state when set validity state handler is called with custom error', () => {
181181
const validityStateHandlerSpy = sinon.spy();
182-
const context = {
183-
form: {
184-
registerInput: validityStateHandlerSpy,
185-
unregisterInput: sandbox.mock().never(),
186-
},
187-
};
188-
const childContextTypes = {
189-
form: PropTypes.object,
182+
const mockForm = {
183+
registerInput: validityStateHandlerSpy,
184+
unregisterInput: sandbox.mock().never(),
190185
};
191186
const error = {
192187
errorCode: 'errorCode',
193188
errorMessage: 'Error Message',
194189
};
195190

196-
const component = mount(<TextArea label="label" name="textarea" value="" />, { context, childContextTypes });
191+
const component = mount(
192+
<FormContext.Provider value={{ form: mockForm }}>
193+
<TextArea label="label" name="textarea" value="" />
194+
</FormContext.Provider>,
195+
);
197196

198197
act(() => {
199198
validityStateHandlerSpy.callArgWith(1, error);
200199
});
201200

202-
expect(component.state('error')).toEqual(error);
201+
expect(component.find('TextArea').first().instance().state.error).toEqual(error);
203202
});
204203

205204
test('should set validity state when set validity state handler is called with ValidityState object', () => {
206205
const validityStateHandlerSpy = sinon.spy();
207-
const context = {
208-
form: {
209-
registerInput: validityStateHandlerSpy,
210-
unregisterInput: sandbox.mock().never(),
211-
},
212-
};
213-
const childContextTypes = {
214-
form: PropTypes.object,
206+
const mockForm = {
207+
registerInput: validityStateHandlerSpy,
208+
unregisterInput: sandbox.mock().never(),
215209
};
216210
const error = {
217211
valid: false,
218212
badInput: true,
219213
};
220214

221-
const component = mount(<TextArea label="label" name="textarea" value="" />, { context, childContextTypes });
215+
const component = mount(
216+
<FormContext.Provider value={{ form: mockForm }}>
217+
<TextArea label="label" name="textarea" value="" />
218+
</FormContext.Provider>,
219+
);
222220

223221
act(() => {
224222
validityStateHandlerSpy.callArgWith(1, error);
225223
});
226-
expect(component.state('error').code).toEqual('badInput');
224+
expect(component.find('TextArea').first().instance().state.error.code).toEqual('badInput');
227225
});
228226
});

0 commit comments

Comments
 (0)