Skip to content
This repository was archived by the owner on Aug 23, 2022. It is now read-only.

Commit 48123c0

Browse files
committed
Adding support for custom wrapper component for Field via <Field component={...}>
1 parent a247e7c commit 48123c0

File tree

3 files changed

+136
-19
lines changed

3 files changed

+136
-19
lines changed

lib/components/field-component.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,18 @@ function createFieldControlComponent(control, props, options) {
262262
control: control }));
263263
}
264264

265+
function getFieldWrapper(props) {
266+
if (!props.component) {
267+
if (props.className || props.children.length > 1) {
268+
return 'div';
269+
}
270+
271+
return null;
272+
}
273+
274+
return props.component;
275+
}
276+
265277
function createFieldClass() {
266278
var customControlPropsMap = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
267279

@@ -283,15 +295,12 @@ function createFieldClass() {
283295
value: function render() {
284296
var props = this.props;
285297

298+
var component = getFieldWrapper(props);
286299

287-
if (props.children.length > 1) {
288-
return _react2.default.createElement(
289-
'div',
290-
props,
291-
_react2.default.Children.map(props.children, function (child) {
292-
return createFieldControlComponent(child, props, options);
293-
})
294-
);
300+
if (component) {
301+
return _react2.default.createElement(component, props, _react2.default.Children.map(props.children, function (child) {
302+
return createFieldControlComponent(child, props, options);
303+
}));
295304
}
296305

297306
return createFieldControlComponent(_react2.default.Children.only(props.children), props, options);
@@ -306,7 +315,8 @@ function createFieldClass() {
306315
updateOn: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.func, _react2.default.PropTypes.oneOf(['change', 'blur', 'focus'])]),
307316
validators: _react2.default.PropTypes.object,
308317
asyncValidators: _react2.default.PropTypes.object,
309-
parser: _react2.default.PropTypes.func
318+
parser: _react2.default.PropTypes.func,
319+
component: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.func, _react2.default.PropTypes.string])
310320
};
311321

312322

src/components/field-component.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,18 @@ function createFieldControlComponent(control, props, options) {
228228
control={control} />;
229229
}
230230

231+
function getFieldWrapper(props) {
232+
if (!props.component) {
233+
if (props.className || props.children.length > 1) {
234+
return 'div';
235+
}
236+
237+
return null;
238+
}
239+
240+
return props.component;
241+
}
242+
231243
export function createFieldClass(
232244
customControlPropsMap = {}
233245
) {
@@ -251,20 +263,24 @@ export function createFieldClass(
251263
]),
252264
validators: React.PropTypes.object,
253265
asyncValidators: React.PropTypes.object,
254-
parser: React.PropTypes.func
266+
parser: React.PropTypes.func,
267+
component: React.PropTypes.oneOfType([
268+
React.PropTypes.func,
269+
React.PropTypes.string
270+
])
255271
};
256272

257273
render() {
258274
const { props } = this;
259-
260-
if (props.children.length > 1) {
261-
return (
262-
<div {...props}>
263-
{ React.Children.map(
264-
props.children,
265-
(child) => createFieldControlComponent(child, props, options))
266-
}
267-
</div>
275+
let component = getFieldWrapper(props);
276+
277+
if (component) {
278+
return React.createElement(
279+
component,
280+
props,
281+
React.Children.map(
282+
props.children,
283+
(child) => createFieldControlComponent(child, props, options))
268284
);
269285
}
270286

test/field-component-spec.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ describe('<Field /> component', () => {
4444
2);
4545
});
4646

47+
it('should not wrap child components in a <div> if only one', () => {
48+
const store = applyMiddleware(thunk)(createStore)(combineReducers({
49+
test: createModelReducer('test', { foo: 'bar' })
50+
}));
51+
const field = TestUtils.renderIntoDocument(
52+
<Provider store={store}>
53+
<Field model="test.foo">
54+
<input />
55+
</Field>
56+
</Provider>
57+
);
58+
59+
assert.throws(() => {
60+
TestUtils.findRenderedDOMComponentWithTag(field, 'div');
61+
});
62+
63+
assert.ok(TestUtils.findRenderedDOMComponentWithTag(field, 'input'));
64+
});
65+
4766
it('should recursively handle nested control components', () => {
4867
const store = applyMiddleware(thunk)(createStore)(combineReducers({
4968
test: createModelReducer('test', { foo: 'bar' })
@@ -651,4 +670,76 @@ describe('<Field /> component', () => {
651670
assert.equal(options.length, 3);
652671
});
653672
});
673+
674+
describe('wrapper components with component property', () => {
675+
const store = applyMiddleware(thunk)(createStore)(combineReducers({
676+
test: createModelReducer('test', {})
677+
}));
678+
679+
it('should wrap children with specified component (string)', () => {
680+
const field = TestUtils.renderIntoDocument(
681+
<Provider store={store}>
682+
<Field component="div">
683+
<input type="text" />
684+
</Field>
685+
</Provider>
686+
);
687+
688+
const wrapper = TestUtils.findRenderedDOMComponentWithTag(field, 'div');
689+
690+
assert.ok(wrapper);
691+
});
692+
693+
it('should wrap children with specified component (class)', () => {
694+
class Wrapper extends React.Component {
695+
render() {
696+
return <main className="wrapper">{ this.props.children }</main>
697+
}
698+
}
699+
700+
const field = TestUtils.renderIntoDocument(
701+
<Provider store={store}>
702+
<Field component={Wrapper}>
703+
<input type="text" />
704+
</Field>
705+
</Provider>
706+
);
707+
708+
const wrapper = TestUtils.findRenderedDOMComponentWithClass(field, 'wrapper');
709+
710+
assert.ok(wrapper);
711+
});
712+
713+
it('should wrap children with specified component (function)', () => {
714+
function Wrapper(props) {
715+
return <section className="wrapper">{props.children}</section>
716+
}
717+
718+
const field = TestUtils.renderIntoDocument(
719+
<Provider store={store}>
720+
<Field component={Wrapper}>
721+
<input type="text" />
722+
</Field>
723+
</Provider>
724+
);
725+
726+
const wrapper = TestUtils.findRenderedDOMComponentWithClass(field, 'wrapper');
727+
728+
assert.ok(wrapper);
729+
});
730+
731+
it('should wrap children with a <div> when provided with className', () => {
732+
const field = TestUtils.renderIntoDocument(
733+
<Provider store={store}>
734+
<Field className="wrapper">
735+
<input type="text" />
736+
</Field>
737+
</Provider>
738+
);
739+
740+
const wrapper = TestUtils.findRenderedDOMComponentWithClass(field, 'wrapper');
741+
742+
assert.ok(wrapper);
743+
});
744+
});
654745
});

0 commit comments

Comments
 (0)