diff --git a/docs/examples/ListGroupCustom.js b/docs/examples/ListGroupCustom.js
new file mode 100644
index 0000000000..d1ea003f64
--- /dev/null
+++ b/docs/examples/ListGroupCustom.js
@@ -0,0 +1,21 @@
+const CustomComponent = React.createClass({
+ render() {
+ return (
+
{}}>
+ {this.props.children}
+
+ );
+ }
+});
+
+const listgroupInstance = (
+
+ Custom Child 1
+ Custom Child 2
+ Custom Child 3
+
+);
+
+React.render(listgroupInstance, mountNode);
diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js
index 9ba1aaf324..21ba550f12 100644
--- a/docs/src/ComponentsPage.js
+++ b/docs/src/ComponentsPage.js
@@ -749,6 +749,15 @@ const ComponentsPage = React.createClass({
Set the header
prop to create a structured item, with a heading and a body area.
+ With custom component children
+
+ When using ListGroupItems directly, ListGroup looks at whether the items have href
+ or onClick props to determine which DOM elements to emit. However, with custom item
+ components as children to ListGroup
, set the
+ componentClass
prop to specify which element ListGroup
should output.
+
+
+
Props
ListGroup
diff --git a/docs/src/Samples.js b/docs/src/Samples.js
index 736daa5ccb..0efd2efe7c 100644
--- a/docs/src/Samples.js
+++ b/docs/src/Samples.js
@@ -81,6 +81,7 @@ export default {
GridBasic: require('fs').readFileSync(__dirname + '/../examples/GridBasic.js', 'utf8'),
ThumbnailAnchor: require('fs').readFileSync(__dirname + '/../examples/ThumbnailAnchor.js', 'utf8'),
ThumbnailDiv: require('fs').readFileSync(__dirname + '/../examples/ThumbnailDiv.js', 'utf8'),
+ ListGroupCustom: require('fs').readFileSync(__dirname + '/../examples/ListGroupCustom.js', 'utf8'),
ListGroupDefault: require('fs').readFileSync(__dirname + '/../examples/ListGroupDefault.js', 'utf8'),
ListGroupLinked: require('fs').readFileSync(__dirname + '/../examples/ListGroupLinked.js', 'utf8'),
ListGroupActive: require('fs').readFileSync(__dirname + '/../examples/ListGroupActive.js', 'utf8'),
diff --git a/src/ListGroup.js b/src/ListGroup.js
index 1a6e330e0f..2c0355dce0 100644
--- a/src/ListGroup.js
+++ b/src/ListGroup.js
@@ -1,4 +1,5 @@
import React, { cloneElement } from 'react';
+import ListGroupItem from './ListGroupItem';
import classNames from 'classnames';
import ValidComponentChildren from './utils/ValidComponentChildren';
@@ -9,6 +10,17 @@ class ListGroup extends React.Component {
(item, index) => cloneElement(item, { key: item.key ? item.key : index })
);
+ if (this.areCustomChildren(items)) {
+ let Component = this.props.componentClass;
+ return (
+
+ {items}
+
+ );
+ }
+
let shouldRenderDiv = false;
if (!this.props.children) {
@@ -21,16 +33,25 @@ class ListGroup extends React.Component {
});
}
- if (shouldRenderDiv) {
- return this.renderDiv(items);
- }
- return this.renderUL(items);
+ return shouldRenderDiv ? this.renderDiv(items) : this.renderUL(items);
}
isAnchorOrButton(props) {
return (props.href || props.onClick);
}
+ areCustomChildren(children) {
+ let customChildren = false;
+
+ ValidComponentChildren.forEach(children, (child) => {
+ if (child.type !== ListGroupItem) {
+ customChildren = true;
+ }
+ }, this);
+
+ return customChildren;
+ }
+
renderUL(items) {
let listItems = ValidComponentChildren.map(items,
(item) => cloneElement(item, { listItem: true })
@@ -56,8 +77,18 @@ class ListGroup extends React.Component {
}
}
+ListGroup.defaultProps = {
+ componentClass: 'div'
+};
+
ListGroup.propTypes = {
className: React.PropTypes.string,
+ /**
+ * The element for ListGroup if children are
+ * user-defined custom components.
+ * @type {("ul"|"div")}
+ */
+ componentClass: React.PropTypes.oneOf(['ul', 'div']),
id: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number
diff --git a/test/ListGroupSpec.js b/test/ListGroupSpec.js
index 13e1a58a8e..13c09c0107 100644
--- a/test/ListGroupSpec.js
+++ b/test/ListGroupSpec.js
@@ -5,145 +5,195 @@ import ListGroupItem from '../src/ListGroupItem';
describe('ListGroup', () => {
- it('Should output a "div" with the class "list-group"', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- );
- assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ describe('All children are of type ListGroupItem', () => {
+
+ it('Should output a "div" with the class "list-group"', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ );
+ assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ });
+
+ it('Should support a single "ListGroupItem" child', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Only Child
+
+ );
+
+ let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
+
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
+ });
+
+ it('Should support a single "ListGroupItem" child contained in an array', () => {
+ let child = [Only Child in array];
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ {child}
+
+ );
+
+ let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
+
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
+ });
+
+ it('Should output a "ul" when single "ListGroupItem" child is a list item', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Only Child
+
+ );
+
+ assert.equal(React.findDOMNode(instance).nodeName, 'UL');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'LI');
+ });
+
+ it('Should output a "div" when single "ListGroupItem" child is an anchor', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Only Child
+
+ );
+
+ assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'A');
+ });
+
+ it('Should support multiple "ListGroupItem" children', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ 1st Child
+ 2nd Child
+
+ );
+
+ let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
+
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[1], 'list-group-item'));
+ });
+
+ it('Should support multiple "ListGroupItem" children including a subset contained in an array', () => {
+ let itemArray = [
+ 2nd Child nested,
+ 3rd Child nested
+ ];
+
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ 1st Child
+ {itemArray}
+ 4th Child
+
+ );
+
+ let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
+
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[1], 'list-group-item'));
+ });
+
+ it('Should output a "ul" when children are list items', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ 1st Child
+ 2nd Child
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ assert.equal(React.findDOMNode(instance).nodeName, 'UL');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'LI');
+ assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'LI');
+ });
+
+
+ it('Should output a "div" when "ListGroupItem" children are anchors and spans', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ 1st Child
+ 2nd Child
+
+ );
+ assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'A');
+ assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'SPAN');
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ });
+
+
+ it('Should output a "div" when "ListGroupItem" children have an onClick handler', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ null}>1st Child
+ 2nd Child
+
+ );
+ assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'BUTTON');
+ assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'SPAN');
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ });
+
+ it('Should support an element id through "id" prop', () => {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Child
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ assert.equal(React.findDOMNode(instance).nodeName, 'UL');
+ assert.equal(React.findDOMNode(instance).id, 'testItem');
+ });
});
- it('Should support a single "ListGroupItem" child', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- Only Child
-
- );
-
- let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
-
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
- });
-
- it('Should support a single "ListGroupItem" child contained in an array', () => {
- let child = [Only Child in array];
- let instance = ReactTestUtils.renderIntoDocument(
-
- {child}
-
- );
-
- let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
-
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
- });
-
- it('Should output a "ul" when single "ListGroupItem" child is a list item', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- Only Child
-
- );
-
- assert.equal(React.findDOMNode(instance).nodeName, 'UL');
- assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'LI');
- });
-
- it('Should output a "div" when single "ListGroupItem" child is an anchor', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- Only Child
-
- );
-
- assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
- assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'A');
- });
-
- it('Should support multiple "ListGroupItem" children', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- 1st Child
- 2nd Child
-
- );
-
- let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
-
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[1], 'list-group-item'));
+ describe('Some or all children are user-defined custom components', () => {
+ it('Should output a div by default when children are custom components', () => {
+ let CustomComponent = React.createClass({
+ render() {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+ });
+
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Child
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
+ assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'LI');
+ });
+
+ it('Should use a "componentClass" prop if specified if any children are custom components', () => {
+ let CustomComponent = React.createClass({
+ render() {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+ });
+
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Custom Child
+ Custom Child
+ RB Child
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
+ assert.equal(React.findDOMNode(instance).nodeName, 'UL');
+ assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'LI');
+ });
});
-
- it('Should support multiple "ListGroupItem" children including a subset contained in an array', () => {
- let itemArray = [
- 2nd Child nested,
- 3rd Child nested
- ];
-
- let instance = ReactTestUtils.renderIntoDocument(
-
- 1st Child
- {itemArray}
- 4th Child
-
- );
-
- let items = ReactTestUtils.scryRenderedComponentsWithType(instance, ListGroupItem);
-
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[0], 'list-group-item'));
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(items[1], 'list-group-item'));
- });
-
- it('Should output a "ul" when children are list items', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- 1st Child
- 2nd Child
-
- );
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
- assert.equal(React.findDOMNode(instance).nodeName, 'UL');
- assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'LI');
- assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'LI');
- });
-
-
- it('Should output a "div" when "ListGroupItem" children are anchors and spans', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- 1st Child
- 2nd Child
-
- );
- assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
- assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'A');
- assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'SPAN');
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
- });
-
-
- it('Should output a "div" when "ListGroupItem" children have an onClick handler', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- null}>1st Child
- 2nd Child
-
- );
- assert.equal(React.findDOMNode(instance).nodeName, 'DIV');
- assert.equal(React.findDOMNode(instance).firstChild.nodeName, 'BUTTON');
- assert.equal(React.findDOMNode(instance).lastChild.nodeName, 'SPAN');
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
- });
-
- it('Should support an element id through "id" prop', () => {
- let instance = ReactTestUtils.renderIntoDocument(
-
- Child
-
- );
- assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'list-group'));
- assert.equal(React.findDOMNode(instance).nodeName, 'UL');
- assert.equal(React.findDOMNode(instance).id, 'testItem');
- });
-
});