From cb3094199f9b0b1746dadaec2b2cea9a28383564 Mon Sep 17 00:00:00 2001 From: Jordan Theriault Date: Tue, 7 Apr 2015 14:33:56 -0400 Subject: [PATCH] Added thumbnail component including docs and tests --- docs/assets/thumbnail.png | Bin 0 -> 3936 bytes docs/assets/thumbnaildiv.png | Bin 0 -> 4156 bytes docs/client.js | 2 ++ docs/examples/.eslintrc | 3 +- docs/examples/ThumbnailAnchor.js | 17 +++++++++++ docs/examples/ThumbnailDiv.js | 38 +++++++++++++++++++++++++ docs/src/ComponentsPage.js | 33 ++++++++++++++++------ docs/src/ReactPlayground.js | 2 ++ docs/src/Samples.js | 2 ++ src/Thumbnail.js | 46 ++++++++++++++++++++++++++++++ src/index.js | 2 ++ src/styleMaps.js | 1 + test/ThumbnailSpec.js | 47 +++++++++++++++++++++++++++++++ 13 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 docs/assets/thumbnail.png create mode 100644 docs/assets/thumbnaildiv.png create mode 100644 docs/examples/ThumbnailAnchor.js create mode 100644 docs/examples/ThumbnailDiv.js create mode 100644 src/Thumbnail.js create mode 100644 test/ThumbnailSpec.js diff --git a/docs/assets/thumbnail.png b/docs/assets/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..6c05a892d40e6b9c0a433b17029341e33c0231a3 GIT binary patch literal 3936 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 literal 0 HcmV?d00001 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 ( + + {this.props.alt} + + ); + } + else { + if(this.props.children) { + return ( +
+ {this.props.alt} +
+ {this.props.children} +
+
+ ); + } + else { + return ( +
+ {this.props.alt} +
+ ); + } + } + } +}); + +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/)); + }); +});