diff --git a/docs/assets/thumbnail.png b/docs/assets/thumbnail.png
new file mode 100644
index 0000000000..6c05a892d4
Binary files /dev/null and b/docs/assets/thumbnail.png differ
diff --git a/docs/assets/thumbnaildiv.png b/docs/assets/thumbnaildiv.png
new file mode 100644
index 0000000000..78b6096acb
Binary files /dev/null and b/docs/assets/thumbnaildiv.png differ
diff --git a/docs/client.js b/docs/client.js
index 0bf1bb699e..ab68372e11 100644
--- a/docs/client.js
+++ b/docs/client.js
@@ -5,6 +5,8 @@ import './assets/style.css';
import './assets/carousel.png';
import './assets/logo.png';
import './assets/favicon.ico';
+import './assets/thumbnail.png';
+import './assets/thumbnaildiv.png';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/theme/solarized.css';
diff --git a/docs/examples/.eslintrc b/docs/examples/.eslintrc
index 86975f07e1..b6dc693611 100644
--- a/docs/examples/.eslintrc
+++ b/docs/examples/.eslintrc
@@ -48,6 +48,7 @@
"Table",
"TabPane",
"Tooltip",
- "Well"
+ "Well",
+ "Thumbnail"
}
}
diff --git a/docs/examples/ThumbnailAnchor.js b/docs/examples/ThumbnailAnchor.js
new file mode 100644
index 0000000000..ccf1764101
--- /dev/null
+++ b/docs/examples/ThumbnailAnchor.js
@@ -0,0 +1,17 @@
+const thumbnailInstance = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+React.render(thumbnailInstance, mountNode);
diff --git a/docs/examples/ThumbnailDiv.js b/docs/examples/ThumbnailDiv.js
new file mode 100644
index 0000000000..dc63bd0dfa
--- /dev/null
+++ b/docs/examples/ThumbnailDiv.js
@@ -0,0 +1,38 @@
+const thumbnailInstance = (
+
+
+
+
+ Thumbnail label
+ Description
+
+
+
+
+
+
+
+
+ Thumbnail label
+ Description
+
+
+
+
+
+
+
+
+ Thumbnail label
+ Description
+
+
+
+
+
+
+
+
+);
+
+React.render(thumbnailInstance, mountNode);
diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js
index 82762d6386..8b7c815d37 100644
--- a/docs/src/ComponentsPage.js
+++ b/docs/src/ComponentsPage.js
@@ -449,6 +449,20 @@ const ComponentsPage = React.createClass({
+ {/* Thumbnail */}
+
+
Thumbnail
+
Thumbnails are designed to showcase linked images with minimal required markup. You can extend the grid component with thumbnails.
+
+
Anchor Thumbnail
+
Creates an anchor wrapping an image.
+
+
+
Divider Thumbnail
+
Creates a divider wrapping an image and other children elements.
+
+
+
{/* ListGroup */}
List group ListGroup, ListGroupItem
@@ -606,15 +620,16 @@ const ComponentsPage = React.createClass({
Alerts
Carousels
Grids
-
List group
-
Labels
-
Badges
-
Jumbotron
-
Page Header
-
Wells
-
Glyphicons
-
Tables
-
Input
+
Thumbnail
+
List group
+
Labels
+
Badges
+
Jumbotron
+
Page Header
+
Wells
+
Glyphicons
+
Tables
+
Input
Back to top
diff --git a/docs/src/ReactPlayground.js b/docs/src/ReactPlayground.js
index 7d2491aa9c..dc707b4d68 100644
--- a/docs/src/ReactPlayground.js
+++ b/docs/src/ReactPlayground.js
@@ -40,6 +40,7 @@ import * as modSplitButton from '../../src/SplitButton';
import * as modTabbedArea from '../../src/TabbedArea';
import * as modTable from '../../src/Table';
import * as modTabPane from '../../src/TabPane';
+import * as modThumbnail from '../../src/Thumbnail';
import * as modTooltip from '../../src/Tooltip';
import * as modWell from '../../src/Well';
@@ -88,6 +89,7 @@ const SplitButton = modSplitButton.default;
const TabbedArea = modTabbedArea.default;
const Table = modTable.default;
const TabPane = modTabPane.default;
+const Thumbnail = modThumbnail.default;
const Tooltip = modTooltip.default;
const Well = modWell.default;
/* eslint-enable */
diff --git a/docs/src/Samples.js b/docs/src/Samples.js
index 4ed0d89fd3..cd25d8d853 100644
--- a/docs/src/Samples.js
+++ b/docs/src/Samples.js
@@ -67,6 +67,8 @@ export default {
CarouselUncontrolled: require('fs').readFileSync(__dirname + '/../examples/CarouselUncontrolled.js', 'utf8'),
CarouselControlled: require('fs').readFileSync(__dirname + '/../examples/CarouselControlled.js', 'utf8'),
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'),
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/Thumbnail.js b/src/Thumbnail.js
new file mode 100644
index 0000000000..ea05cf85d2
--- /dev/null
+++ b/src/Thumbnail.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import classSet from 'classnames';
+import BootstrapMixin from './BootstrapMixin';
+
+const Thumbnail = React.createClass({
+ mixins: [BootstrapMixin],
+
+ getDefaultProps() {
+ return {
+ bsClass: 'thumbnail'
+ };
+ },
+
+ render() {
+ let classes = this.getBsClassSet();
+
+ if(this.props.href) {
+ return (
+
+
+
+ );
+ }
+ else {
+ if(this.props.children) {
+ return (
+
+
+
+ {this.props.children}
+
+
+ );
+ }
+ else {
+ return (
+
+
+
+ );
+ }
+ }
+ }
+});
+
+export default Thumbnail;
diff --git a/src/index.js b/src/index.js
index 1f2bd5590b..118bacd7d9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -48,6 +48,7 @@ import SubNav from './SubNav';
import TabbedArea from './TabbedArea';
import Table from './Table';
import TabPane from './TabPane';
+import Thumbnail from './Thumbnail';
import Tooltip from './Tooltip';
import Well from './Well';
import styleMaps from './styleMaps';
@@ -103,6 +104,7 @@ export default {
TabbedArea,
Table,
TabPane,
+ Thumbnail,
Tooltip,
Well,
styleMaps
diff --git a/src/styleMaps.js b/src/styleMaps.js
index 67992ae22d..54694e58ed 100644
--- a/src/styleMaps.js
+++ b/src/styleMaps.js
@@ -9,6 +9,7 @@ const styleMaps = {
'form': 'form',
'glyphicon': 'glyphicon',
'label': 'label',
+ 'thumbnail': 'thumbnail',
'list-group-item': 'list-group-item',
'panel': 'panel',
'panel-group': 'panel-group',
diff --git a/test/ThumbnailSpec.js b/test/ThumbnailSpec.js
new file mode 100644
index 0000000000..ba805cf2ea
--- /dev/null
+++ b/test/ThumbnailSpec.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import ReactTestUtils from 'react/lib/ReactTestUtils';
+import Thumbnail from '../src/Thumbnail';
+
+describe('Thumbnail', function () {
+ it('Should have a thumbnail class and be an anchor', function () {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ );
+ assert.ok(instance.getDOMNode().className.match(/\bthumbnail\b/));
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a'));
+ });
+
+ it('Should have an image', function () {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'img'));
+ });
+
+ it('Should have a thumbnail class and be a div', function () {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ );
+ assert.ok(instance.getDOMNode().className.match(/\bthumbnail\b/));
+ assert.equal(instance.getDOMNode().nodeName, 'DIV');
+ });
+
+ it('Should have an image', function () {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ );
+ assert.ok(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'img'));
+ });
+
+ it('Should have an inner div with class caption', function () {
+ let instance = ReactTestUtils.renderIntoDocument(
+
+ Test
+
+ Test child element
+
+
+ );
+ assert.ok(instance.getDOMNode().lastChild.className.match(/\bcaption\b/));
+ });
+});