├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples └── basic │ ├── index.html │ ├── index.js │ ├── package.json │ ├── server.js │ └── webpack.config.js ├── lib ├── components │ ├── BaseImage.js │ └── Image.js ├── index.js └── utils.js ├── package.json ├── test ├── client │ └── image.js ├── misc │ └── utils.js └── server │ └── image.js └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src 3 | ./**/*/node_modules 4 | dist 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/lab/lib/linters/eslint/.eslintrc", 3 | "ecmaFeatures": { 4 | "jsx": true, 5 | "arrowFunctions": true, 6 | }, 7 | "env": { 8 | "node": true, 9 | "browser": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Users Environment Variables 5 | .lock-wscript 6 | 7 | # Distribution does not belong in repo 8 | dist 9 | 10 | coverage.html 11 | npm-debug.log 12 | src 13 | 14 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | webpack.config.js 2 | scripts 3 | examples 4 | .travis.yml 5 | lib 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | - "0.12" 6 | - "iojs" 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.0.4 (Upcoming) 2 | - Get to 100% test coverage 3 | 4 | ## v0.0.3 5 | - update to react 0.13 6 | - simplify codebase 7 | - ensure test infrastructure is properly set up with recent changes 8 | - rename .js to .jsx for components 9 | - npm prepublish step 10 | 11 | ## v0.0.2 12 | - debounce method on resize events of 150ms 13 | - fix bug in renderNative where src attribute would show the srcset information 14 | - some internal reordering based on initial test framework 15 | 16 | ## v0.0.1 17 | - Initial implement of Image component 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lois Desplat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-picture 2 | [![Build Status](https://travis-ci.org/ldesplat/react-picture.svg?branch=master)](https://travis-ci.org/ldesplat/react-picture) 3 | 4 | ## This component is not maintained anymore but its source code may be of use to some people 5 | 6 | You should take a look at https://github.com/reacttraining/react-media as an example on how the React community has been able to make a more abstracted version of this very same concept. As before, you should also check the Alternative Libraries section below for some more inspiration. 7 | 8 | ## Description 9 | 10 | React component for rendering responsive images. Attempts to implement `` support in non-compliant browsers, otherwize renders the native ``. Will eventually support Picture & Source elements. 11 | 12 | ## How to use 13 | 14 | ```npm install react-picture``` 15 | 16 | Then use in your react app like so: 17 | 18 | ``` 19 | var Img = require('react-picture').Image; 20 | ... 21 | render: function() { 22 | var imgs = '//placebacon.net/200/150 600w, //placebacon.net/300/300 800w'; 23 | 24 | return ( 25 | Your picture description 26 | ); 27 | } 28 | ... 29 | ``` 30 | 31 | The following properties can be passed to the Image component: 32 | - `alt` - Required and describes your picture 33 | - `srcSet` - Required and consists of a string formated like srcset 34 | - `extra` - Optional object which contains properties that will be put on img tag 35 | 36 | ## Features 37 | 38 | - Use `` when supported 39 | - Implement srcset support for browsers that do not have it, using javascript. Currently missing sizes implementation. 40 | 41 | ## Roadmap 42 | 43 | ### 0.1.0 44 | - Implement sizes attribute 45 | 46 | ### 1.0 47 | - Stable API 48 | - Correct implementation of match method 49 | - ~~Verify srcset feature detection~~ 50 | - ie8 & up browser support (w/React polyfills only) 51 | - Performance optimizations 52 | - ~~Ismorphic support~~ 53 | - 100% test coverage 54 | - ~~Travis CI~~ 55 | 56 | ### To be discussed 57 | - PictureContainer 58 | - Karma tests 59 | 60 | ### 2.0 61 | - Picture element 62 | - Source element 63 | 64 | ## References 65 | 66 | - [The picture element](http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#the-picture-element) 67 | - [The img element](http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#the-img-element) 68 | 69 | ## Alternative libraries 70 | 71 | - If you are not using react in your app, [BBC-News/Imager.js](https://github.com/BBC-News/Imager.js) is a very good library 72 | - srcset polyfill [borismus/srcset-polyfill](https://github.com/borismus/srcset-polyfill) 73 | - picture polyfill [scottjehl/picturefill](https://github.com/scottjehl/picturefill) 74 | 75 | ## Acknowledgements 76 | 77 | - React's excellent library & documentation 78 | - Ryan Florence's components on github which were stolen to start up (webpack setup, browserify, scripts) 79 | - [This excellent article](http://www.html5rocks.com/en/mobile/high-dpi/) 80 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | react-picture example 4 | 5 | 6 |

react-picture

7 |

Response images using React

8 |
9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/basic/index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var RP = require('react-picture'); 3 | 4 | var App = React.createClass({ 5 | 6 | imgs: [ 7 | '//placebacon.net/200/150 600w', 8 | '//placebacon.net/300/300 800w', 9 | '//placebacon.net/400/400 1000w' 10 | ], 11 | 12 | render: function () { 13 | 14 | return ( 15 |
16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | 23 | }); 24 | 25 | React.render(, document.getElementById('root')); 26 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-picture-example", 3 | "version": "1.0.0", 4 | "description": "react-picture-example-description", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/ldesplat/react-picture.git" 12 | }, 13 | "keywords": [ 14 | "react-picture-keywords" 15 | ], 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/ldesplat/react-picture/issues" 19 | }, 20 | "homepage": "https://github.com/ldesplat/react-picture", 21 | "dependencies": { 22 | "react": "^0.13.3" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^5.6.18", 26 | "babel-loader": "^5.1.4", 27 | "node-libs-browser": "^0.5.2", 28 | "react-hot-loader": "^1.2.7", 29 | "webpack": "^1.9.11", 30 | "webpack-dev-server": "^1.9.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/basic/server.js: -------------------------------------------------------------------------------- 1 | var Webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var Config = require('./webpack.config'); 4 | 5 | new WebpackDevServer(Webpack(Config), { 6 | publicPath: Config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true, 9 | stats: { 10 | colors: true 11 | } 12 | }).listen(3000, 'localhost', function (err) { 13 | 14 | if (err) { 15 | console.log(err); 16 | } 17 | 18 | console.log('Listening at localhost:3000'); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/basic/webpack.config.js: -------------------------------------------------------------------------------- 1 | var Path = require('path'); 2 | var Webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'eval', 6 | entry: [ 7 | 'webpack-dev-server/client?http://localhost:3000', 8 | 'webpack/hot/only-dev-server', 9 | './index' 10 | ], 11 | output: { 12 | path: Path.join(__dirname, 'dist'), 13 | filename: 'bundle.js', 14 | publicPath: '/static/' 15 | }, 16 | plugins: [ 17 | new Webpack.HotModuleReplacementPlugin(), 18 | new Webpack.NoErrorsPlugin() 19 | ], 20 | resolve: { 21 | alias: { 22 | 'react-picture': Path.join(__dirname, '..', '..', 'lib') 23 | }, 24 | extensions: ['', '.js'] 25 | }, 26 | module: { 27 | loaders: [{ 28 | test: /\.js$/, 29 | loaders: ['react-hot', 'babel'], 30 | exclude: /node_modules/, 31 | include: __dirname 32 | }, { 33 | test: /\.js$/, 34 | loaders: ['babel'], 35 | include: Path.join(__dirname, '..', '..', 'lib') 36 | }] 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/components/BaseImage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Utils = require('../utils'); 3 | 4 | 5 | /** Equivalent to html element 6 | * 7 | * 8 | */ 9 | var BaseImageComponent = module.exports = React.createClass({ 10 | 11 | 12 | propTypes: { 13 | srcSet: React.PropTypes.string.isRequired, 14 | alt: React.PropTypes.string.isRequired, 15 | extra: React.PropTypes.object, 16 | nativeSupport: React.PropTypes.string 17 | }, 18 | 19 | 20 | getDefaultProps: function () { 21 | 22 | return { 23 | extra: {}, 24 | nativeSupport: 'none' 25 | }; 26 | }, 27 | 28 | 29 | getInitialState: function () { 30 | 31 | return { 32 | w: Utils.getWidth(), 33 | x: Utils.getDensity() 34 | }; 35 | }, 36 | 37 | 38 | componentDidMount: function () { 39 | 40 | if (this.props.nativeSupport !== 'full') { 41 | window.addEventListener('resize', this.resizeThrottler, false); 42 | } 43 | }, 44 | 45 | 46 | componentWillUnmount: function () { 47 | 48 | if (!this.props.nativeSupport !== 'full') { 49 | window.removeEventListener('resize', this.resizeThrottler, false); 50 | } 51 | }, 52 | 53 | 54 | render: function () { 55 | 56 | if (!this.props.srcSet) { 57 | return null; 58 | } 59 | 60 | var candidates = BaseImageComponent._buildCandidates(this.props.srcSet); 61 | 62 | if (this.props.nativeSupport === 'full') { 63 | return this.renderNative(candidates); 64 | } 65 | 66 | return ( 67 | {this.props.alt} 68 | ); 69 | }, 70 | 71 | 72 | renderNative: function (candidates) { 73 | 74 | return ( 75 | {this.props.alt} 76 | ); 77 | }, 78 | 79 | 80 | // Taken from https://developer.mozilla.org/en-US/docs/Web/Events/resize 81 | resizing: false, 82 | resizeThrottler: function () { 83 | 84 | if (!this.resizing) { 85 | this.resizing = true; 86 | 87 | if (window && window.requestAnimationFrame) { 88 | window.requestAnimationFrame(this.onResize); 89 | } else { 90 | setTimeout(this.onResize, 66); 91 | } 92 | } 93 | }, 94 | 95 | 96 | onResize: function () { 97 | 98 | this.setState({ w: Utils.getWidth(), x: Utils.getDensity() }); 99 | this.resizing = false; 100 | }, 101 | 102 | 103 | statics: { 104 | 105 | _buildCandidates: function (srcset) { 106 | 107 | return srcset.split(',').map(function (srcImg) { 108 | 109 | var stringComponents = srcImg.trim().split(' '); 110 | var candidate = { 111 | url: stringComponents[0].trim(), 112 | w: 0, 113 | x: 1.0 114 | }; 115 | 116 | for (var i = 1; i < stringComponents.length; i++) { 117 | var str = stringComponents[i].trim(); 118 | if (str.indexOf('w', str.length - 1) !== -1) { 119 | candidate.w = parseInt(str.substring(0, str.length - 1)); 120 | } else if (str.indexOf('x', str.length - 1) !== -1) { 121 | candidate.x = parseFloat(str.substring(0, str.length - 1)); 122 | } else { 123 | console.warn('Invalid parameter passed to Image srcSet: [' + str + '] in ' + srcImg); 124 | } 125 | } 126 | 127 | return candidate; 128 | }); 129 | }, 130 | 131 | 132 | __compare: function (a, b, state, accessorFn) { 133 | 134 | var aDt = accessorFn(a) - state; 135 | var bDt = accessorFn(b) - state; 136 | 137 | if ((aDt === 0 && bDt !== 0) || // a perfectly matches target but b does not 138 | (bDt < 0 && aDt >= 0)) // b is less than target but a is the same or better 139 | { 140 | return a; 141 | } 142 | 143 | if ((bDt === 0 && aDt !== 0) || // b perfectly matches target but a does not 144 | (aDt < 0 && bDt >= 0)) // a is less than target but b is the same or better 145 | { 146 | return b; 147 | } 148 | 149 | if (Math.abs(aDt) < Math.abs(bDt)) 150 | { 151 | return a; 152 | } 153 | 154 | if (Math.abs(bDt) < Math.abs(aDt)) 155 | { 156 | return b; 157 | } 158 | 159 | return a; 160 | }, 161 | 162 | 163 | _matchImage: function (candidates, width, density) { 164 | 165 | if (!candidates || candidates.length === 0) { 166 | return null; 167 | } 168 | 169 | var image = candidates.reduce(function (a, b) { 170 | 171 | if (a.x === b.x) { 172 | // Both have the same density so attempt to find a better one using width 173 | if (a.w === b.w) { 174 | return a; // hey, it came first! 175 | } 176 | 177 | return BaseImageComponent.__compare(a, b, width, img => img.w); 178 | } 179 | 180 | return BaseImageComponent.__compare(a, b, density, img => img.x); 181 | }).url; 182 | 183 | return image; 184 | } 185 | } 186 | }); 187 | -------------------------------------------------------------------------------- /lib/components/Image.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BaseImage = require('./BaseImage'); 3 | 4 | 5 | /** Equivalent to html element 6 | * 7 | * 8 | */ 9 | var ImageComponent = module.exports = React.createClass({ 10 | 11 | 12 | propTypes: { 13 | srcSet: React.PropTypes.string.isRequired, 14 | alt: React.PropTypes.string.isRequired, 15 | extra: React.PropTypes.object, 16 | nativeSupport: React.PropTypes.string 17 | }, 18 | 19 | 20 | getDefaultProps: function () { 21 | // Calculate nativeSupport in getDefaultProps so we don't 22 | // check this everytime the component is mounted despite 23 | // making it much harder for testing 24 | var nativeSupport = 'full'; 25 | if (typeof document !== 'undefined') { 26 | var img = document.createElement('img'); 27 | ('srcset' in img) ? nativeSupport = 'partial' : nativeSupport = 'none'; 28 | (nativeSupport === 'partial' && 'sizes' in img) ? nativeSupport = 'full' : nativeSupport = 'partial'; 29 | } 30 | 31 | return { 32 | extra: {}, 33 | nativeSupport: nativeSupport 34 | }; 35 | }, 36 | 37 | 38 | render: function () { 39 | 40 | return ; 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports.BaseImage = require('./components/BaseImage'); 2 | module.exports.Image = require('./components/Image'); 3 | //module.exports.Picture= require('./components/Picture'); 4 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports.getWidth = function () { 5 | 6 | if (typeof window !== 'undefined') { 7 | return window.innerWidth || document.documentElement.clientWidth; 8 | } 9 | 10 | return 0; 11 | }; 12 | 13 | 14 | module.exports.getDensity = function () { 15 | 16 | if (typeof window !== 'undefined') { 17 | return window.devicePixelRatio || 1; 18 | } 19 | 20 | return 1; 21 | }; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-picture", 3 | "version": "0.0.4", 4 | "description": "Responsive images component for React.JS", 5 | "main": "./src/index", 6 | "scripts": { 7 | "clean": "rimraf src dist", 8 | "build": "babel -d src/ lib/", 9 | "build:umd": "webpack lib/index.js dist/react-picture.js && NODE_ENV=production webpack lib/index.js dist/react-picture.min.js", 10 | "test": "lab -S -L -T node_modules/lab-babel -t 70", 11 | "test-cov-html": "lab -c -S -T node_modules/lab-babel -r html -o coverage.html", 12 | "prepublish": "npm run test && npm run clean && npm run build && npm run build:umd" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/ldesplat/react-picture.git" 17 | }, 18 | "keywords": [ 19 | "image", 20 | "picture", 21 | "responsive", 22 | "srcset", 23 | "react", 24 | "react-component" 25 | ], 26 | "author": "Lois Desplat", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/ldesplat/react-picture/issues" 30 | }, 31 | "homepage": "https://github.com/ldesplat/react-picture", 32 | "devDependencies": { 33 | "babel": "5.x.x", 34 | "babel-core": "5.x.x", 35 | "babel-loader": "5.x.x", 36 | "code": "1.x.x", 37 | "lab": "5.x.x", 38 | "lab-babel": "1.x.x", 39 | "react": "0.13.x", 40 | "rimraf": "^2.4.3", 41 | "webpack": "1.x.x", 42 | "webpack-dev-server": "1.x.x" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/client/image.js: -------------------------------------------------------------------------------- 1 | /*var React = require('react/addons'); 2 | var Code = require('code'); 3 | var Lab = require('lab'); 4 | 5 | // Test shortcuts 6 | var lab = exports.lab = Lab.script(); 7 | 8 | var expect = Code.expect; 9 | var before = lab.before; 10 | var after = lab.after; 11 | var beforeEach = lab.beforeEach; 12 | var afterEach = lab.afterEach; 13 | var describe = lab.experiment; 14 | var it = lab.test; 15 | 16 | var Img = require('../../lib/index').Image; 17 | 18 | 19 | var internals = { 20 | sampleSrcSet: 'http://fancyserver.com/image.jpg 600w, http://fancyserver.com/image2.jpg 1000w' 21 | }; 22 | 23 | 24 | internals.exDOM = function () { 25 | 26 | return document.getElementById('example'); 27 | }; 28 | 29 | 30 | describe('JSDOM - Native -', function () { 31 | 32 | before(function (done) { 33 | 34 | document.createElement = function (name) { 35 | 36 | var element = Object.getPrototypeOf(this).createElement.call(this); 37 | if (name === 'img') { 38 | element.srcset = ''; 39 | } 40 | 41 | return element; 42 | }; 43 | 44 | done(); 45 | }); 46 | 47 | 48 | after(function (done) { 49 | 50 | document.createElement = function (name) { 51 | 52 | return Object.getPrototypeOf(this).createElement.call(this); 53 | }; 54 | 55 | done(); 56 | }); 57 | 58 | afterEach(function (done) { 59 | 60 | React.unmountComponentAtNode(internals.exDOM()); 61 | done(); 62 | }); 63 | 64 | 65 | it('renders a null image', function (done) { 66 | 67 | React.render(, internals.exDOM()); 68 | expect(internals.exDOM().innerHTML).to.not.contain('img'); 69 | done(); 70 | }); 71 | 72 | 73 | it('render a proper image', function (done) { 74 | 75 | React.render(text, internals.exDOM()); 76 | var html = internals.exDOM().innerHTML; 77 | 78 | expect(html).to.contain('alt="text"'); 79 | expect(html).to.contain('src="http://fancyserver.com/image.jpg"'); 80 | expect(html).to.contain('srcset="http://fancyserver.com/image.jpg 600w, http://fancyserver.com/image2.jpg 1000w"'); 81 | done(); 82 | }); 83 | }); 84 | 85 | describe('JSDOM - Not native -', function () { 86 | 87 | afterEach(function (done) { 88 | 89 | React.unmountComponentAtNode(internals.exDOM()); 90 | done(); 91 | }); 92 | 93 | 94 | it('renders a null image', function (done) { 95 | 96 | React.render(, internals.exDOM()); 97 | expect(internals.exDOM().innerHTML).to.not.contain('img'); 98 | done(); 99 | }); 100 | 101 | 102 | it('render a proper image', function (done) { 103 | 104 | React.render(text, internals.exDOM()); 105 | var html = internals.exDOM().innerHTML; 106 | 107 | expect(html).to.contain('alt="text"'); 108 | expect(html).to.contain('src="http://fancyserver.com/image2.jpg"'); 109 | expect(html).to.not.contain('srcset'); 110 | done(); 111 | }); 112 | 113 | }); 114 | */ 115 | -------------------------------------------------------------------------------- /test/misc/utils.js: -------------------------------------------------------------------------------- 1 | var Code = require('code'); 2 | var Lab = require('lab'); 3 | 4 | // Test shortcuts 5 | var lab = exports.lab = Lab.script(); 6 | 7 | var expect = Code.expect; 8 | var describe = lab.experiment; 9 | var it = lab.test; 10 | 11 | var Utils = require('../../lib/utils'); 12 | 13 | describe('Utils methods -', function () { 14 | 15 | it('gets width', function (done) { 16 | 17 | var width = Utils.getWidth(); 18 | expect(width).to.equal(0); 19 | done(); 20 | }); 21 | 22 | it('gets density', function (done) { 23 | 24 | var density = Utils.getDensity(); 25 | expect(density).to.equal(1); 26 | done(); 27 | }); 28 | 29 | it('gets width with document.documentElement', function (done) { 30 | 31 | global.window = {}; 32 | global.document = { 33 | documentElement: { 34 | clientWidth: 1024 35 | } 36 | }; 37 | 38 | var width = Utils.getWidth(); 39 | 40 | delete global.window; 41 | delete global.document; 42 | 43 | expect(width).to.equal(1024); 44 | done(); 45 | }); 46 | 47 | it('gets width with window.innerWidth', function (done) { 48 | 49 | global.window = { 50 | innerWidth: 256 51 | }; 52 | 53 | var width = Utils.getWidth(); 54 | 55 | delete global.window; 56 | 57 | expect(width).to.equal(256); 58 | done(); 59 | }); 60 | 61 | 62 | it('gets density with window.devicePixelRatio', function (done) { 63 | 64 | global.window = { 65 | devicePixelRatio: 2 66 | }; 67 | 68 | var density = Utils.getDensity(); 69 | 70 | delete global.window; 71 | 72 | expect(density).to.equal(2); 73 | done(); 74 | }); 75 | 76 | it('gets density with window but no devicePixelRatio', function (done) { 77 | 78 | global.window = {}; 79 | 80 | var density = Utils.getDensity(); 81 | 82 | delete global.window; 83 | 84 | expect(density).to.equal(1); 85 | done(); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/server/image.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Code = require('code'); 3 | var Lab = require('lab'); 4 | 5 | 6 | // Test shortcuts 7 | var lab = exports.lab = Lab.script(); 8 | var expect = Code.expect; 9 | var describe = lab.experiment; 10 | var it = lab.test; 11 | 12 | 13 | var internals = { 14 | sampleSrcSet: 'http://fancyserver.com/image.jpg 600w, http://fancyserver.com/image2.jpg 1000w', 15 | nativeOutput: 'text', 16 | renderOutput: 'text' 17 | }; 18 | 19 | 20 | var Img = require('../../lib/index').Image; 21 | 22 | 23 | describe('Image Component - Testing as NodeJS', function () { 24 | 25 | it('uses default native support', function (done) { 26 | 27 | var img = React.renderToStaticMarkup( 28 | text 29 | ); 30 | 31 | expect(img).to.equal(internals.nativeOutput); 32 | 33 | done(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Webpack = require('webpack'); 4 | 5 | var plugins = [ 6 | new Webpack.optimize.OccurenceOrderPlugin(), 7 | new Webpack.DefinePlugin({ 8 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 9 | }) 10 | ]; 11 | 12 | if (process.env.NODE_ENV === 'production') { 13 | plugins.push( 14 | new Webpack.optimize.UglifyJsPlugin({ 15 | compressor: { 16 | 'screw_ie8': true, 17 | warnings: false 18 | } 19 | }) 20 | ); 21 | } 22 | 23 | module.exports = { 24 | module: { 25 | loaders: [{ 26 | test: /\.js$/, 27 | loaders: ['babel-loader'], 28 | exclude: /node_modules/ 29 | }] 30 | }, 31 | output: { 32 | library: 'react-picture', 33 | libraryTarget: 'umd' 34 | }, 35 | plugins: plugins, 36 | resolve: { 37 | extensions: ['', '.js'] 38 | } 39 | }; 40 | --------------------------------------------------------------------------------