├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── bin ├── README.dist.template.md ├── clean-dist.js ├── csv-parse-census.js └── generate-dist-readme.js ├── common.js ├── package.json ├── resources ├── census_data_20160104_568ac7b494753.csv └── data.json ├── src ├── assets │ └── images │ │ ├── d3-logo.png │ │ ├── github-retina.png │ │ ├── github.png │ │ ├── react-logo.svg │ │ ├── twitter-retina.png │ │ └── twitter.png ├── bootstrap.js ├── components │ ├── CountriesChartPanel │ │ └── CountriesChartPanel.js │ ├── Footer │ │ └── Footer.js │ ├── Header │ │ └── Header.js │ ├── LifeExpectancy │ │ └── LifeExpectancy.js │ ├── Navigator │ │ └── injectNavigator.js │ ├── ReactFauxDom │ │ ├── StaticMultiLineChart │ │ │ └── StaticMultiLineChart.js │ │ └── __tests__ │ │ │ ├── StaticMultiLineChart.spec.js │ │ │ └── __snapshots__ │ │ │ └── StaticMultiLineChart.spec.js.snap │ ├── Root │ │ └── Root.js │ ├── Select │ │ ├── ColoredMultiSelect.js │ │ ├── Select.js │ │ ├── index.js │ │ └── style │ │ │ └── ColoredMultiSelect.scss │ ├── Slider │ │ └── Slider.js │ ├── TwitterButton │ │ └── TwitterButton.js │ ├── ViewSourceOnGithub │ │ ├── ViewSourceOnGithub.js │ │ └── viewSourceOnGithub.css │ ├── WindowInfos │ │ ├── Provider.js │ │ ├── index.js │ │ └── injectWindowInfos.js │ ├── d3 │ │ ├── StaticMultiLineChart │ │ │ └── StaticMultiLineChart.js │ │ ├── TransitionMultiLineChart │ │ │ └── TransitionMultiLineChart.js │ │ └── __tests__ │ │ │ ├── StaticMultiLineChart.spec.js │ │ │ ├── TransitionMultiLineChart.spec.js │ │ │ └── __snapshots__ │ │ │ ├── StaticMultiLineChart.spec.js.snap │ │ │ └── TransitionMultiLineChart.spec.js.snap │ ├── d3act │ │ ├── BarChartPanel │ │ │ └── BarChartPanel.js │ │ └── MixedChartPanel │ │ │ ├── MixedChartPanel.js │ │ │ └── MixedChartPanel.scss │ ├── recharts │ │ ├── TransitionMultiLineChart │ │ │ └── TransitionMultiLineChart.js │ │ └── __tests__ │ │ │ ├── TransitionMultiLineChart.spec.js │ │ │ └── __snapshots__ │ │ │ └── TransitionMultiLineChart.spec.js.snap │ └── victory │ │ ├── CountNpmDownloadsChart │ │ └── CountNpmDownloadsChart.js │ │ ├── TransitionMultiLineChart │ │ └── TransitionMultiLineChart.js │ │ ├── WorldPopulationByAgeRange │ │ └── WorldPopulationByAgeRange.js │ │ └── __tests__ │ │ ├── TransitionMultiLineChart.spec.js │ │ ├── WorldPopulationByAgeRange.spec.js │ │ └── __snapshots__ │ │ ├── TransitionMultiLineChart.spec.js.snap │ │ └── WorldPopulationByAgeRange.spec.js.snap ├── containers │ ├── App │ │ └── App.js │ ├── D3StaticMultiLineChart │ │ └── D3StaticMultiLineChart.js │ ├── D3TransitionMultiLineChart │ │ └── D3TransitionMultiLineChart.js │ ├── D3actBarChart │ │ └── D3actBarChart.js │ ├── D3actMixedChart │ │ └── D3actMixedChart.js │ ├── Home │ │ ├── Home.js │ │ └── __tests__ │ │ │ ├── Home.spec.js │ │ │ └── __snapshots__ │ │ │ └── Home.spec.js.snap │ ├── ReactFauxDomStaticMultiLineChart │ │ └── ReactFauxDomStaticMultiLineChart.js │ ├── RechartsTransitionMultiLineChart │ │ └── RechartsTransitionMultiLineChart.js │ ├── VictoryCountNpmDownloads │ │ └── VictoryCountNpmDownloads.js │ ├── VictoryTransitionMultiLineChart │ │ └── VictoryTransitionMultiLineChart.js │ ├── VictoryWorldPopulationByAgeRange │ │ └── VictoryWorldPopulationByAgeRange.js │ └── index.js ├── index.ejs ├── resources │ ├── fixtures │ │ ├── census-data-compressed.json │ │ ├── census-data-extended.json │ │ ├── life-expectancy.csv │ │ └── stackoverflow-survey.json │ ├── helper.js │ └── loaders.js ├── routes.js ├── style │ ├── footer.scss │ ├── header.scss │ ├── main.scss │ └── select.scss ├── test │ └── jestHelpers.js └── utils │ └── helpers.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "production": { 4 | "presets": ["es2015", "react"], 5 | "plugins": ["transform-class-properties", "transform-es2015-destructuring", "transform-object-rest-spread", "add-module-exports"] 6 | }, 7 | "test": { 8 | "presets": ["es2015", "react"], 9 | "plugins": ["transform-class-properties", "transform-es2015-destructuring", "transform-object-rest-spread", "add-module-exports"] 10 | }, 11 | // only enable it when process.env.NODE_ENV is 'development' or undefined 12 | "development": { 13 | "presets": ["es2015", "react"], 14 | "plugins": ["transform-class-properties", "transform-es2015-destructuring", "transform-object-rest-spread", "add-module-exports", "react-hot-loader/babel"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | # change these settings to your own preference 6 | indent_style = space 7 | indent_size = 2 8 | 9 | # it's recommend to keep these unchanged 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [{package,bower}.json] 19 | indent_style = space 20 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/libs/sensorsChecker.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true, 4 | "jasmine": true, 5 | "protractor": true, 6 | "browser": true 7 | }, 8 | "globals": { 9 | "goToUrl": true, 10 | "waitUntilIsElementPresent": true 11 | }, 12 | "extends": "airbnb", 13 | "parser": "babel-eslint", 14 | "rules": { 15 | // disable requiring trailing commas because it might be nice to revert to 16 | // being JSON at some point, and I don't want to make big changes now. 17 | "comma-dangle": 0, 18 | "brace-style": [2, "stroustrup"], 19 | "no-console": 0, 20 | "padded-blocks": 0, 21 | "indent": [2, 2, {"SwitchCase": 1}], 22 | "spaced-comment": 1, 23 | "max-len": 0, 24 | "arrow-parens": 0, 25 | "react/forbid-prop-types": 0, 26 | "no-mixed-operators": 0, 27 | "react/jsx-filename-extension": 0, 28 | "no-confusing-arrow": 0, 29 | "react/jsx-max-props-per-line": [1, { "when": "multiline" }], 30 | "no-return-assign": 0, 31 | "no-param-reassign": 0, 32 | "no-plusplus": 0, 33 | "import/no-extraneous-dependencies": 0, // needed in case of using some devDependencies 34 | "import/prefer-default-export": 0, 35 | "global-require": 0, 36 | "react/require-default-props": 0 // @todo enforce it ? 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | build 4 | *.log 5 | *.swp 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | - "6" 5 | before_script: 6 | - npm run build 7 | - npm run build-prod-all 8 | script: 9 | - npm test 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | d3-react-experiments 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/topheman/d3-react-experiments.svg?branch=master)](https://travis-ci.org/topheman/d3-react-experiments) 5 | ![Release](https://img.shields.io/badge/release-v3-blue.svg) 6 | 7 | 8 | 9 | ### Introduction 10 | 11 | As I explain bellow, there are multiple approaches to handle d3 with React. The goal of this repo is to explore them and provide examples: 12 | 13 | * to test those different approaches 14 | * to help others choose the one that fit their needs 15 | 16 | What will you find in that project ? 17 | 18 | * Some [Recharts](https://topheman.github.io/d3-react-experiments/#/recharts/transition-multi-line-chart) based charts 🆕 19 | * Some [basic](https://topheman.github.io/d3-react-experiments/#/victory/world-population-by-age-range) and [advanced](https://topheman.github.io/d3-react-experiments/#/victory/count-npm-downloads) Victory based charts 20 | * Some [vanilla d3](https://topheman.github.io/d3-react-experiments/#/d3) components based charts 21 | * Some [react-faux-dom based](https://topheman.github.io/d3-react-experiments/#/d3/react-faux-dom) component charts 22 | * Some [d3act](https://topheman.github.io/d3-react-experiments/#/d3act) based charts 23 | * Unit tests with [Jest](#tests) 24 | 25 | Some blog posts I wrote about this project: 26 | 27 | * [D3 React Components with Victory – Reusability / Composability](http://dev.topheman.com/d3-react-components-with-victory-reusability-composability) 28 | * [Plain d3 code and React working together](http://dev.topheman.com/d3-react-chart-components/) 29 | 30 | What's new from v2 ? 31 | 32 | * Upgraded to [webpack2](https://webpack.js.org/) 33 | * Setup [react-hot-loader@3](https://gaearon.github.io/react-hot-loader/getstarted/) 34 | * Upgraded to latest [eslint](http://eslint.org/) + latest [eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb) 35 | 36 | [This is still a work in progress](https://topheman.github.io/d3-react-experiments/#/), more examples / blog posts will come ... 37 | 38 | ### React & D3 39 | 40 | [D3 (data driven documents)](http://d3js.org/) is a JavaScript library that helps you build visualisations. It is very powerfull (most of the JavaScript datavisualization libraries are based on it). It handles the data you pass it and mutates the DOM. 41 | 42 | With [React](https://facebook.github.io/react/index.html), on the other hand, you never access directly the DOM and let it manage the changes as well as the events. 43 | 44 | So, by default, the two of them don't really get along ... d3 messes up with [React's reconciliation](https://facebook.github.io/react/docs/reconciliation.html) and React removes what d3 is appending to the DOM ... 45 | 46 | In the last year a lot of projects have risen with the goal to make those two work gently together, but there isn't a clear winner yet. 47 | 48 | There are two main approaches, both of them using d3 for the processing: 49 | 50 | * blackbox d3 and let it do the render without messing up with React lifecyle 51 | * reimplement the job done by d3 on the DOM by letting React do the render (wrapping svg inside jsx) 52 | 53 | Both approaches have their pros and cons (I won't talk about that here - people with more experience in that field than me have written posts on that). 54 | 55 | ### Setup 56 | 57 | This project now follows the same development workflow as the one explained in [topheman/webpack-babel-starter](https://github.com/topheman/webpack-babel-starter) (the v2 using **webpack 2**). 58 | 59 | #### Install 60 | 61 | ```shell 62 | git clone https://github.com/topheman/d3-react-experiments.git 63 | cd d3-react-experiments 64 | yarn 65 | ``` 66 | 67 | #### Run 68 | 69 | ##### From localhost 70 | 71 | `npm start` 72 | 73 | ##### From your network ip 74 | 75 | Useful to access the website via wifi from other devices such as smartphones. 76 | 77 | `LOCALHOST=false npm start` 78 | 79 | #### Build 80 | 81 | At the root of the project : 82 | 83 | * `npm run build`: for debug (like in dev - with sourceMaps and all) 84 | * `npm run build-prod`: for production (minified/optimized ...) 85 | * `npm run build-prod-all`: both at once in the same build (with sourcemaps on dev version) 86 | 87 | A `/build/dist` folder will be created with your project built in it. 88 | 89 | You can run it with `npm run serve-build` 90 | 91 | #### Linter 92 | 93 | I'm using eslint, based on [eslint-config-airbnb](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb), a preset for `.eslintrc` configuration. For more infos, checkout the release it was implemented: [react-es6-redux@2.5.0](https://github.com/topheman/react-es6-redux/releases/tag/v2.5.0). 94 | 95 | * `npm run lint`: single run linting of `/src` & `/test` folders 96 | * `npm run lint-watch`: same in watch mode 97 | 98 | You can disable the linter by `LINTER=false npm start` (though it will still be run a pre-commit hook) 99 | 100 | #### Tests 101 | 102 | This project is unit tested using the [Jest](https://facebook.github.io/jest/) framework: 103 | 104 | * To run those tests: `npm test` 105 | * To run those tests in watch mode: `npm run jest-watch` 106 | * To update snapshots: `npm run jest -- -u` 107 | 108 | Those unit tests are run each time you commit and will be run on [travis-ci](https://travis-ci.org/topheman/d3-react-experiments) when you'll push. 109 | 110 | ## Resources 111 | 112 | * [http://stackoverflow.com/research/developer-survey-2015](http://stackoverflow.com/research/developer-survey-2015) 113 | * [http://www.census.gov/](http://www.census.gov/population/international/data/idb/informationGateway.php): I wrote a little routine to process the csv 114 | * [ourworldindata.org](https://ourworldindata.org) 115 | * [npm registry](https://docs.npmjs.com/misc/registry) 116 | * [topheman/webpack-babel-starter](https://github.com/topheman/webpack-babel-starter) 117 | 118 | -------------------------------------------------------------------------------- /bin/README.dist.template.md: -------------------------------------------------------------------------------- 1 | ## d3-react-experiments - distribution version (gh-pages branch) 2 | 3 | This is the distribution version of [topheman/d3-react-experiments](https://github.com/topheman/d3-react-experiments) - v<%= pkg.version %><% if (urlToCommit !== null) { %> - [#<%= gitRevisionShort %>](<%= urlToCommit %>)<% } %>. 4 | 5 | **Warning**: This is the **generated** code, versionned on the `gh-pages` branch, testable online [here](https://topheman.github.io/d3-react-experiments/). If you wish to see the original source code, switch to the [master branch](https://github.com/topheman/d3-react-experiments). 6 | 7 | ### Infos: 8 | 9 | Those informations are available on the [topheman/webpack-babel-starter](https://github.com/topheman/webpack-babel-starter) project: 10 | 11 | * [How those files where generated (Readme - build section)](https://github.com/topheman/webpack-babel-starter#build) 12 | * [How to deploy your generated version (Wiki - deploy section)](https://github.com/topheman/webpack-babel-starter/wiki#deploy) 13 | 14 | As explained in the [README](https://github.com/topheman/webpack-babel-starter#build), when you `npm run build-prod-all`, two versions will be generated: 15 | 16 | * One at the root (the production version) 17 | * One in the [devtools folder](https://github.com/topheman/d3-react-experiments/tree/gh-pages/devtools), which contains as you'll see sourcemaps and are not minified. 18 | 19 | Test the demo [here](https://topheman.github.io/d3-react-experiments/). 20 | 21 | ------ 22 | 23 | You can disable the generation of this file by removing the following line in the `package.json`: 24 | 25 | ```js 26 | "postbuild-prod-all": "npm run generate-dist-readme" 27 | ``` 28 | 29 | You can customize the output of this file, the template is located at `bin/README.dist.template.md`. 30 | -------------------------------------------------------------------------------- /bin/clean-dist.js: -------------------------------------------------------------------------------- 1 | const log = require('npmlog'); 2 | log.level = 'silly'; 3 | const common = require('../common'); 4 | 5 | const ROOT_DIR = common.getRootDir(); 6 | 7 | /** run */ 8 | 9 | log.info('clean-dist', `Cleaning ...`); 10 | const deleted = require('del').sync([ 11 | ROOT_DIR + '/build/dist/*', 12 | ROOT_DIR + '/build/dist/**/*', 13 | ROOT_DIR + '/build/dist/!.git/**/*' 14 | ]); 15 | deleted.forEach(function(e){ 16 | console.log(e); 17 | }); 18 | -------------------------------------------------------------------------------- /bin/csv-parse-census.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A little script to turn the csv from http://www.census.gov/population/international/data/idb/informationGateway.php 3 | * to json 4 | * 5 | * To use it just: 6 | * node bin/csv-parse-census.js --source ./resources/census_data_20160104_568ac7b494753.csv --output-dir ./src/resources/fixtures 7 | */ 8 | 9 | var parse = require('csv-parse'); 10 | var fs = require('fs'); 11 | var argv = require('minimist')(process.argv.slice(2)); 12 | 13 | if (!argv.source || !argv['output-dir']) { 14 | console.log('--source or --output-dir is missing'); 15 | process.exit(1); 16 | } 17 | 18 | var outputExtended = []; 19 | var outputCompressed = { 20 | headers: [], 21 | data: [] 22 | }; 23 | 24 | // Create the parser 25 | var parser = parse({delimiter: ','}); 26 | 27 | // Use the writable stream api 28 | var fileLineNumber = 1; 29 | var headers; 30 | parser.on('readable', function(){ 31 | while(record = parser.read()){ 32 | if (fileLineNumber === 2) { 33 | headers = record; 34 | outputCompressed.headers = headers; 35 | } 36 | if (fileLineNumber > 2) { 37 | outputExtended.push(record.reduce(function(acc, curr, index) { 38 | var toNumeric = parseFloat(curr); 39 | acc[headers[index]] = isNaN(toNumeric) || headers[index] === 'Age' ? curr : toNumeric; 40 | return acc; 41 | }, {})); 42 | outputCompressed.data.push(record.map(function(curr, index) { 43 | var toNumeric = parseFloat(curr); 44 | return isNaN(toNumeric) || headers[index] === 'Age' ? curr : toNumeric; 45 | })); 46 | } 47 | fileLineNumber++; 48 | } 49 | }); 50 | 51 | // Catch any error 52 | parser.on('error', function(err){ 53 | console.log(err.message); 54 | }); 55 | 56 | // When we are done, test that the parsed output matched what expected 57 | parser.on('finish', function(){ 58 | var outputDir = argv['output-dir'].replace(/(\/+)$/,"");// remove trailing slashes 59 | var outputExtendedFile = outputDir + '/census-data-extended.json'; 60 | fs.writeFileSync(outputExtendedFile, JSON.stringify(outputExtended)); 61 | console.log('Saved at ' + outputExtendedFile); 62 | var outputCompressedFile = outputDir + '/census-data-compressed.json'; 63 | fs.writeFileSync(outputCompressedFile, JSON.stringify(outputCompressed)); 64 | console.log('Saved at ' + outputCompressedFile); 65 | }); 66 | 67 | var input = fs.createReadStream(argv.source); 68 | input.pipe(parser); 69 | -------------------------------------------------------------------------------- /bin/generate-dist-readme.js: -------------------------------------------------------------------------------- 1 | const log = require('npmlog'); 2 | log.level = 'silly'; 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const common = require('../common'); 6 | const _ = {template: require('lodash.template')}; 7 | var template; 8 | 9 | const OUTPUT_DIR = './build/dist'; 10 | 11 | try { 12 | template = fs.readFileSync(__dirname + '/README.dist.template.md', 'utf8').toString(); 13 | } 14 | catch (e) { 15 | log.error('generate-dist-readme', e.message); 16 | process.exit(1); 17 | } 18 | 19 | const infos = common.getInfos(); 20 | 21 | template = _.template(template); 22 | 23 | const compiled = template(infos); 24 | 25 | try { 26 | fs.writeFileSync(path.resolve(__dirname, '..', OUTPUT_DIR, 'README.md'), compiled); 27 | log.info('generate-dist-readme', 'Create README.md file for gh-pages at ' + path.resolve(__dirname, '..', OUTPUT_DIR, 'README.md')); 28 | } 29 | catch(e) { 30 | log.error('generate-dist-readme', e.message); 31 | process.exit(1); 32 | } 33 | -------------------------------------------------------------------------------- /common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | function getRootDir() { 3 | return __dirname; 4 | } 5 | 6 | function getInfos() { 7 | const gitActive = projectIsGitManaged(); 8 | const gitRev = require('git-rev-sync'); 9 | const moment = require('moment'); 10 | const pkg = require('./package.json'); 11 | const infos = { 12 | pkg: pkg, 13 | today: moment(new Date()).format(), 14 | year: new Date().toISOString().substr(0, 4), 15 | gitRevisionShort: gitActive ? gitRev.short() : null, 16 | gitRevisionLong: gitActive ? gitRev.long() : null, 17 | author: (pkg.author && pkg.author.name) ? pkg.author.name : (pkg.author || null), 18 | urlToCommit: null 19 | }; 20 | infos.urlToCommit = gitActive ? _getUrlToCommit(pkg, infos.gitRevisionLong) : null; 21 | return infos; 22 | } 23 | 24 | /** 25 | * Called in default mode by webpack (will format it correctly in comments) 26 | * Called in formatted mode by gulp (for html comments) 27 | * @param {String} mode default/formatted 28 | * @returns {String} 29 | */ 30 | function getBanner(mode) { 31 | const infos = getInfos(); 32 | const compiled = [ 33 | infos.pkg.name, 34 | '', 35 | infos.pkg.description, 36 | '', 37 | `@version v${infos.pkg.version} - ${infos.today}`, 38 | (infos.gitRevisionShort !== null ? `@revision #${infos.gitRevisionShort}` : '') + (infos.urlToCommit !== null ? ` - ${infos.urlToCommit}` : ''), 39 | (infos.author !== null ? `@author ${infos.author}` : ''), 40 | `@copyright ${infos.year}(c)` + (infos.author !== null ? ` ${infos.author}` : ''), 41 | (infos.pkg.license ? `@license ${infos.pkg.license}` : ''), 42 | '' 43 | ].join(mode === 'formatted' ? '\n * ' : '\n'); 44 | return compiled; 45 | } 46 | 47 | function getBannerHtml() { 48 | return '\n'; 49 | } 50 | 51 | function projectIsGitManaged() { 52 | const fs = require('fs'); 53 | const path = require('path'); 54 | try { 55 | // Query the entry 56 | const stats = fs.lstatSync(path.join(__dirname,'.git')); 57 | 58 | // Is it a directory? 59 | if (stats.isDirectory()) { 60 | return true; 61 | } 62 | return false; 63 | } 64 | catch (e) { 65 | return false; 66 | } 67 | } 68 | 69 | function _getUrlToCommit(pkg, gitRevisionLong){ 70 | let urlToCommit = null; 71 | // if no repository return null 72 | if (typeof pkg.repository === 'undefined') { 73 | return urlToCommit; 74 | } 75 | //retrieve and reformat repo url from package.json 76 | if (typeof(pkg.repository) === 'string') { 77 | urlToCommit = pkg.repository; 78 | } 79 | else if (typeof(pkg.repository.url) === 'string') { 80 | urlToCommit = pkg.repository.url; 81 | } 82 | //check that there is a git repo specified in package.json & it is a github one 83 | if (urlToCommit !== null && /^https:\/\/github.com/.test(urlToCommit)) { 84 | urlToCommit = urlToCommit.replace(/.git$/, '/tree/' + gitRevisionLong);//remove the .git at the end 85 | } 86 | return urlToCommit; 87 | } 88 | 89 | module.exports.getRootDir = getRootDir; 90 | module.exports.getInfos = getInfos; 91 | module.exports.getBanner = getBanner; 92 | module.exports.getBannerHtml = getBannerHtml; 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-react-experiments", 3 | "version": "3.0.0", 4 | "description": "Mix d3 & react", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "test": "npm run lint && npm run jest -- --silent", 8 | "jest-watch": "./node_modules/.bin/jest --watch", 9 | "jest": "./node_modules/.bin/jest", 10 | "start": "DEVTOOLS=true npm run webpack", 11 | "lint": "./node_modules/.bin/eslint src test", 12 | "lint-fix": "./node_modules/.bin/eslint --fix src --ext .js", 13 | "lint-watch": "./node_modules/.bin/esw --watch src test", 14 | "clean-dist": "node ./bin/clean-dist.js", 15 | "serve-build": "echo 'Serving distribution folder build/dist' && npm run serve-dist", 16 | "serve-dist": "./node_modules/.bin/serve build/dist", 17 | "build": "npm run clean-dist && NODE_ENV=production OPTIMIZE=false DEVTOOLS=true npm run webpack-build", 18 | "build-prod": "npm run clean-dist && NODE_ENV=production npm run webpack-build-prod", 19 | "build-prod-all": "DEVTOOLS=false npm run build-prod && NODE_ENV=production OPTIMIZE=false DEVTOOLS=true DIST_DIR=dist/devtools npm run webpack-build", 20 | "postbuild-prod-all": "npm run generate-dist-readme", 21 | "webpack": "./node_modules/.bin/webpack-dev-server --progress --colors --hot --inline", 22 | "webpack-build": "./node_modules/.bin/webpack --progress", 23 | "webpack-build-prod": "./node_modules/.bin/webpack --progress -p", 24 | "generate-dist-readme": "node bin/generate-dist-readme.js", 25 | "generate-fixtures": "node ./bin/csv-parse-census.js --source ./resources/census_data_20160104_568ac7b494753.csv --output-dir ./src/resources/fixtures", 26 | "postinstall": "echo \"If you're using npm2 please upgrade to npm3, see Victory error message. Also don't care about the warning messages about peerDependencies related to builder-victory-component\"" 27 | }, 28 | "pre-commit": [ 29 | "test" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/topheman/d3-react-experiments.git" 34 | }, 35 | "author": "Christophe Rosset", 36 | "license": "MIT", 37 | "devDependencies": { 38 | "babel-core": "^6.5.2", 39 | "babel-eslint": "^7.2.3", 40 | "babel-loader": "^6.2.3", 41 | "babel-plugin-add-module-exports": "^0.1.2", 42 | "babel-plugin-transform-class-properties": "^6.6.0", 43 | "babel-plugin-transform-es2015-destructuring": "^6.6.5", 44 | "babel-plugin-transform-object-rest-spread": "^6.6.5", 45 | "babel-preset-es2015": "^6.5.0", 46 | "babel-preset-react": "^6.5.0", 47 | "css-loader": "^0.28.4", 48 | "csv-parse": "^1.2.0", 49 | "del": "^2.2.0", 50 | "eslint": "^3.x.x", 51 | "eslint-config-airbnb": "^15.0.1", 52 | "eslint-loader": "^1.8.0", 53 | "eslint-plugin-import": "^2.6.0", 54 | "eslint-plugin-jsx-a11y": "^5.0.3", 55 | "eslint-plugin-react": "^7.1.0", 56 | "eslint-watch": "^2.1.4", 57 | "extract-text-webpack-plugin": "^2.1.2", 58 | "file-loader": "^0.11.2", 59 | "git-rev-sync": "^1.4.0", 60 | "html-webpack-plugin": "^2.9.0", 61 | "json-loader": "^0.5.3", 62 | "lodash.template": "^4.2.2", 63 | "minimist": "^1.2.0", 64 | "moment": "^2.11.2", 65 | "my-local-ip": "^1.0.0", 66 | "node-sass": "^4.5.3", 67 | "npmlog": "^2.0.2", 68 | "pre-commit": "^1.1.2", 69 | "react-hot-loader": "next", 70 | "sass-loader": "^6.0.6", 71 | "serve": "^1.4.0", 72 | "style-loader": "^0.18.2", 73 | "url-loader": "^0.5.9", 74 | "webpack": "^2.6.1", 75 | "webpack-dev-server": "^2.4.5" 76 | }, 77 | "dependencies": { 78 | "array-includes": "^3.0.2", 79 | "babel-jest": "^19.0.0", 80 | "bootstrap-sass": "^3.3.5", 81 | "color-hash": "^1.0.3", 82 | "d3-array": "^1.0.1", 83 | "d3-axis": "^1.0.3", 84 | "d3-request": "^1.0.2", 85 | "d3-scale": "^1.0.3", 86 | "d3-selection": "^1.0.2", 87 | "d3-shape": "^1.0.3", 88 | "d3-transition": "^1.0.2", 89 | "d3act": "^2.0.0", 90 | "enzyme": "^2.8.0", 91 | "enzyme-to-json": "^1.5.0", 92 | "es6-promise": "^3.0.2", 93 | "jest-cli": "^19.0.2", 94 | "radium": "^0.16.2", 95 | "react": "^15.1.0", 96 | "react-addons-test-utils": "15.3.2", 97 | "react-dom": "15.3.2", 98 | "react-faux-dom": "^3.0.0", 99 | "react-router": "^2.4.1", 100 | "react-scroll": "^1.4.0", 101 | "react-select": "^1.0.0-rc.1", 102 | "recharts": "^0.21.2", 103 | "victory": "^0.12.1" 104 | }, 105 | "jest": { 106 | "transform": { 107 | ".*": "/node_modules/babel-jest" 108 | }, 109 | "unmockedModulePathPatterns": [ 110 | "/node_modules" 111 | ], 112 | "moduleFileExtensions": [ 113 | "js", 114 | "json" 115 | ], 116 | "roots": [ 117 | "/src/" 118 | ] 119 | }, 120 | "private": true 121 | } 122 | -------------------------------------------------------------------------------- /resources/census_data_20160104_568ac7b494753.csv: -------------------------------------------------------------------------------- 1 | " Broad Age Groups and Sex for World - Aggregated" 2 | Region,Country,Year,Age,"Both Sexes Population","Male Population","Female Population","Percent Both Sexes","Percent Male","Percent Female","Sex Ratio" 3 | World,Aggregated,2010,Total,6866332358,3457247166,3409085192,100.0,100.0,100.0,101.4 4 | World,Aggregated,2010,0-14,1829719792,947202489,882517303,26.6,27.4,25.9,107.3 5 | World,Aggregated,2010,15-64,4503398859,2273874198,2229524661,65.6,65.8,65.4,102.0 6 | World,Aggregated,2010,65+,533213707,236170479,297043228,7.8,6.8,8.7,79.5 7 | World,Aggregated,2011,Total,6944055583,3496356311,3447699272,100.0,100.0,100.0,101.4 8 | World,Aggregated,2011,0-14,1834103960,949425476,884678484,26.4,27.2,25.7,107.3 9 | World,Aggregated,2011,15-64,4564419436,2305073803,2259345633,65.7,65.9,65.5,102.0 10 | World,Aggregated,2011,65+,545532187,241857032,303675155,7.9,6.9,8.8,79.6 11 | World,Aggregated,2012,Total,7022349283,3535826157,3486523126,100.0,100.0,100.0,101.4 12 | World,Aggregated,2012,0-14,1839671314,952230406,887440908,26.2,26.9,25.5,107.3 13 | World,Aggregated,2012,15-64,4621209386,2334330290,2286879096,65.8,66.0,65.6,102.1 14 | World,Aggregated,2012,65+,561468583,249265461,312203122,8.0,7.0,9.0,79.8 15 | World,Aggregated,2013,Total,7101027895,3575479240,3525548655,100.0,100.0,100.0,101.4 16 | World,Aggregated,2013,0-14,1846112604,955432637,890679967,26.0,26.7,25.3,107.3 17 | World,Aggregated,2013,15-64,4676636473,2362996109,2313640364,65.9,66.1,65.6,102.1 18 | World,Aggregated,2013,65+,578278818,257050494,321228324,8.1,7.2,9.1,80.0 19 | World,Aggregated,2014,Total,7178722893,3614623955,3564098938,100.0,100.0,100.0,101.4 20 | World,Aggregated,2014,0-14,1853017964,958829618,894188346,25.8,26.5,25.1,107.2 21 | World,Aggregated,2014,15-64,4729747302,2390611938,2339135364,65.9,66.1,65.6,102.2 22 | World,Aggregated,2014,65+,595957627,265182399,330775228,8.3,7.3,9.3,80.2 23 | World,Aggregated,2015,Total,7256490011,3653920784,3602569227,100.0,100.0,100.0,101.4 24 | World,Aggregated,2015,0-14,1860463578,962504434,897959144,25.6,26.3,24.9,107.2 25 | World,Aggregated,2015,15-64,4779884483,2416898840,2362985643,65.9,66.1,65.6,102.3 26 | World,Aggregated,2015,65+,616141950,274517510,341624440,8.5,7.5,9.5,80.4 27 | World,Aggregated,2016,Total,7334771614,3693280349,3641491265,100.0,100.0,100.0,101.4 28 | World,Aggregated,2016,0-14,1868438922,966405822,902033100,25.5,26.2,24.8,107.1 29 | World,Aggregated,2016,15-64,4830623741,2443497159,2387126582,65.9,66.2,65.6,102.4 30 | World,Aggregated,2016,65+,635708951,283377368,352331583,8.7,7.7,9.7,80.4 31 | World,Aggregated,2017,Total,7412778971,3732422544,3680356427,100.0,100.0,100.0,101.4 32 | World,Aggregated,2017,0-14,1876749474,970452250,906297224,25.3,26.0,24.6,107.1 33 | World,Aggregated,2017,15-64,4878438096,2468640794,2409797302,65.8,66.1,65.5,102.4 34 | World,Aggregated,2017,65+,657591401,293329500,364261901,8.9,7.9,9.9,80.5 35 | World,Aggregated,2018,Total,7490427640,3771310557,3719117083,100.0,100.0,100.0,101.4 36 | World,Aggregated,2018,0-14,1884901719,974381709,910520010,25.2,25.8,24.5,107.0 37 | World,Aggregated,2018,15-64,4925337413,2493394363,2431943050,65.8,66.1,65.4,102.5 38 | World,Aggregated,2018,65+,680188508,303534485,376654023,9.1,8.0,10.1,80.6 39 | World,Aggregated,2019,Total,7567402977,3809785418,3757617559,100.0,100.0,100.0,101.4 40 | World,Aggregated,2019,0-14,1892402228,977936108,914466120,25.0,25.7,24.3,106.9 41 | World,Aggregated,2019,15-64,4970908006,2517524823,2453383183,65.7,66.1,65.3,102.6 42 | World,Aggregated,2019,65+,704092743,314324487,389768256,9.3,8.3,10.4,80.6 43 | World,Aggregated,2020,Total,7643402123,3847702175,3795699948,100.0,100.0,100.0,101.4 44 | World,Aggregated,2020,0-14,1899155782,981062194,918093588,24.8,25.5,24.2,106.9 45 | World,Aggregated,2020,15-64,5014616520,2540779058,2473837462,65.6,66.0,65.2,102.7 46 | World,Aggregated,2020,65+,729629821,325860923,403768898,9.5,8.5,10.6,80.7 47 | World,Aggregated,2021,Total,7718256830,3884971535,3833285295,100.0,100.0,100.0,101.3 48 | World,Aggregated,2021,0-14,1904978506,983663823,921314683,24.7,25.3,24.0,106.8 49 | World,Aggregated,2021,15-64,5059390977,2564672827,2494718150,65.6,66.0,65.1,102.8 50 | World,Aggregated,2021,65+,753887347,336634885,417252462,9.8,8.7,10.9,80.7 51 | World,Aggregated,2022,Total,7792021317,3921600380,3870420937,100.0,100.0,100.0,101.3 52 | World,Aggregated,2022,0-14,1909654905,985620463,924034442,24.5,25.1,23.9,106.7 53 | World,Aggregated,2022,15-64,5103107220,2588042724,2515064496,65.5,66.0,65.0,102.9 54 | World,Aggregated,2022,65+,779259192,347937193,431321999,10.0,8.9,11.1,80.7 55 | World,Aggregated,2023,Total,7864725370,3957588451,3907136919,100.0,100.0,100.0,101.3 56 | World,Aggregated,2023,0-14,1913225406,986958744,926266662,24.3,24.9,23.7,106.6 57 | World,Aggregated,2023,15-64,5145715457,2610784796,2534930661,65.4,66.0,64.9,103.0 58 | World,Aggregated,2023,65+,805784507,359844911,445939596,10.2,9.1,11.4,80.7 59 | World,Aggregated,2024,Total,7936271554,3992887649,3943383905,100.0,100.0,100.0,101.3 60 | World,Aggregated,2024,0-14,1916135671,987920308,928215363,24.1,24.7,23.5,106.4 61 | World,Aggregated,2024,15-64,5191332498,2635051426,2556281072,65.4,66.0,64.8,103.1 62 | World,Aggregated,2024,65+,828803385,369915915,458887470,10.4,9.3,11.6,80.6 63 | World,Aggregated,2025,Total,8006580553,4027458694,3979121859,100.0,100.0,100.0,101.2 64 | World,Aggregated,2025,0-14,1918459040,988539800,929919240,24.0,24.5,23.4,106.3 65 | World,Aggregated,2025,15-64,5235721968,2658675690,2577046278,65.4,66.0,64.8,103.2 66 | World,Aggregated,2025,65+,852376824,380236427,472140397,10.6,9.4,11.9,80.5 67 | World,Aggregated,2026,Total,8075716000,4061352955,4014363045,100.0,100.0,100.0,101.2 68 | World,Aggregated,2026,0-14,1920135783,988800324,931335459,23.8,24.3,23.2,106.2 69 | World,Aggregated,2026,15-64,5281741663,2683172469,2598569194,65.4,66.1,64.7,103.3 70 | World,Aggregated,2026,65+,873838554,389380162,484458392,10.8,9.6,12.1,80.4 71 | World,Aggregated,2027,Total,8143729466,4094613125,4049116341,100.0,100.0,100.0,101.1 72 | World,Aggregated,2027,0-14,1921055741,988677963,932377778,23.6,24.1,23.0,106.0 73 | World,Aggregated,2027,15-64,5323867670,2705647499,2618220171,65.4,66.1,64.7,103.3 74 | World,Aggregated,2027,65+,898806055,400287663,498518392,11.0,9.8,12.3,80.3 75 | World,Aggregated,2028,Total,8210559895,4127207443,4083352452,100.0,100.0,100.0,101.1 76 | World,Aggregated,2028,0-14,1921320500,988223928,933096572,23.4,23.9,22.9,105.9 77 | World,Aggregated,2028,15-64,5355950734,2723020071,2632930663,65.2,66.0,64.5,103.4 78 | World,Aggregated,2028,65+,933252586,415955138,517297448,11.4,10.1,12.7,80.4 79 | World,Aggregated,2029,Total,8276190519,4159129959,4117060560,100.0,100.0,100.0,101.0 80 | World,Aggregated,2029,0-14,1920983422,987469055,933514367,23.2,23.7,22.7,105.8 81 | World,Aggregated,2029,15-64,5389053369,2740912169,2648141200,65.1,65.9,64.3,103.5 82 | World,Aggregated,2029,65+,966153728,430748735,535404993,11.7,10.4,13.0,80.5 83 | World,Aggregated,2030,Total,8340606590,4190376720,4150229870,100.0,100.0,100.0,101.0 84 | World,Aggregated,2030,0-14,1919946650,986361865,933584785,23.0,23.5,22.5,105.7 85 | World,Aggregated,2030,15-64,5422608614,2758948186,2663660428,65.0,65.8,64.2,103.6 86 | World,Aggregated,2030,65+,998051326,445066669,552984657,12.0,10.6,13.3,80.5 87 | World,Aggregated,2031,Total,8403880343,4220984684,4182895659,100.0,100.0,100.0,100.9 88 | World,Aggregated,2031,0-14,1918437104,985024348,933412756,22.8,23.3,22.3,105.5 89 | World,Aggregated,2031,15-64,5455751431,2776682675,2679068756,64.9,65.8,64.0,103.6 90 | World,Aggregated,2031,65+,1029691808,459277661,570414147,12.3,10.9,13.6,80.5 91 | World,Aggregated,2032,Total,8466094022,4250995647,4215098375,100.0,100.0,100.0,100.9 92 | World,Aggregated,2032,0-14,1916701648,983588364,933113284,22.6,23.1,22.1,105.4 93 | World,Aggregated,2032,15-64,5489972927,2794825488,2695147439,64.8,65.7,63.9,103.7 94 | World,Aggregated,2032,65+,1059419447,472581795,586837652,12.5,11.1,13.9,80.5 95 | World,Aggregated,2033,Total,8527246205,4280411563,4246834642,100.0,100.0,100.0,100.8 96 | World,Aggregated,2033,0-14,1914921585,982148765,932772820,22.5,22.9,22.0,105.3 97 | World,Aggregated,2033,15-64,5521671011,2811561016,2710109995,64.8,65.7,63.8,103.7 98 | World,Aggregated,2033,65+,1090653609,486701782,603951827,12.8,11.4,14.2,80.6 99 | World,Aggregated,2034,Total,8587325154,4309229068,4278096086,100.0,100.0,100.0,100.7 100 | World,Aggregated,2034,0-14,1913252246,980786741,932465505,22.3,22.8,21.8,105.2 101 | World,Aggregated,2034,15-64,5549813190,2826523773,2723289417,64.6,65.6,63.7,103.8 102 | World,Aggregated,2034,65+,1124259718,501918554,622341164,13.1,11.6,14.5,80.7 103 | -------------------------------------------------------------------------------- /resources/data.json: -------------------------------------------------------------------------------- 1 | [{"Region":"World","Country":"Aggregated","Year":2010,"Age":"Total","Both Sexes Population":6866332358,"Male Population":3457247166,"Female Population":3409085192,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2010,"Age":"0-14","Both Sexes Population":1829719792,"Male Population":947202489,"Female Population":882517303,"Percent Both Sexes":26.6,"Percent Male":27.4,"Percent Female":25.9,"Sex Ratio":107.3},{"Region":"World","Country":"Aggregated","Year":2010,"Age":"15-64","Both Sexes Population":4503398859,"Male Population":2273874198,"Female Population":2229524661,"Percent Both Sexes":65.6,"Percent Male":65.8,"Percent Female":65.4,"Sex Ratio":102},{"Region":"World","Country":"Aggregated","Year":2010,"Age":"65+","Both Sexes Population":533213707,"Male Population":236170479,"Female Population":297043228,"Percent Both Sexes":7.8,"Percent Male":6.8,"Percent Female":8.7,"Sex Ratio":79.5},{"Region":"World","Country":"Aggregated","Year":2011,"Age":"Total","Both Sexes Population":6944055583,"Male Population":3496356311,"Female Population":3447699272,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2011,"Age":"0-14","Both Sexes Population":1834103960,"Male Population":949425476,"Female Population":884678484,"Percent Both Sexes":26.4,"Percent Male":27.2,"Percent Female":25.7,"Sex Ratio":107.3},{"Region":"World","Country":"Aggregated","Year":2011,"Age":"15-64","Both Sexes Population":4564419436,"Male Population":2305073803,"Female Population":2259345633,"Percent Both Sexes":65.7,"Percent Male":65.9,"Percent Female":65.5,"Sex Ratio":102},{"Region":"World","Country":"Aggregated","Year":2011,"Age":"65+","Both Sexes Population":545532187,"Male Population":241857032,"Female Population":303675155,"Percent Both Sexes":7.9,"Percent Male":6.9,"Percent Female":8.8,"Sex Ratio":79.6},{"Region":"World","Country":"Aggregated","Year":2012,"Age":"Total","Both Sexes Population":7022349283,"Male Population":3535826157,"Female Population":3486523126,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2012,"Age":"0-14","Both Sexes Population":1839671314,"Male Population":952230406,"Female Population":887440908,"Percent Both Sexes":26.2,"Percent Male":26.9,"Percent Female":25.5,"Sex Ratio":107.3},{"Region":"World","Country":"Aggregated","Year":2012,"Age":"15-64","Both Sexes Population":4621209386,"Male Population":2334330290,"Female Population":2286879096,"Percent Both Sexes":65.8,"Percent Male":66,"Percent Female":65.6,"Sex Ratio":102.1},{"Region":"World","Country":"Aggregated","Year":2012,"Age":"65+","Both Sexes Population":561468583,"Male Population":249265461,"Female Population":312203122,"Percent Both Sexes":8,"Percent Male":7,"Percent Female":9,"Sex Ratio":79.8},{"Region":"World","Country":"Aggregated","Year":2013,"Age":"Total","Both Sexes Population":7101027895,"Male Population":3575479240,"Female Population":3525548655,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2013,"Age":"0-14","Both Sexes Population":1846112604,"Male Population":955432637,"Female Population":890679967,"Percent Both Sexes":26,"Percent Male":26.7,"Percent Female":25.3,"Sex Ratio":107.3},{"Region":"World","Country":"Aggregated","Year":2013,"Age":"15-64","Both Sexes Population":4676636473,"Male Population":2362996109,"Female Population":2313640364,"Percent Both Sexes":65.9,"Percent Male":66.1,"Percent Female":65.6,"Sex Ratio":102.1},{"Region":"World","Country":"Aggregated","Year":2013,"Age":"65+","Both Sexes Population":578278818,"Male Population":257050494,"Female Population":321228324,"Percent Both Sexes":8.1,"Percent Male":7.2,"Percent Female":9.1,"Sex Ratio":80},{"Region":"World","Country":"Aggregated","Year":2014,"Age":"Total","Both Sexes Population":7178722893,"Male Population":3614623955,"Female Population":3564098938,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2014,"Age":"0-14","Both Sexes Population":1853017964,"Male Population":958829618,"Female Population":894188346,"Percent Both Sexes":25.8,"Percent Male":26.5,"Percent Female":25.1,"Sex Ratio":107.2},{"Region":"World","Country":"Aggregated","Year":2014,"Age":"15-64","Both Sexes Population":4729747302,"Male Population":2390611938,"Female Population":2339135364,"Percent Both Sexes":65.9,"Percent Male":66.1,"Percent Female":65.6,"Sex Ratio":102.2},{"Region":"World","Country":"Aggregated","Year":2014,"Age":"65+","Both Sexes Population":595957627,"Male Population":265182399,"Female Population":330775228,"Percent Both Sexes":8.3,"Percent Male":7.3,"Percent Female":9.3,"Sex Ratio":80.2},{"Region":"World","Country":"Aggregated","Year":2015,"Age":"Total","Both Sexes Population":7256490011,"Male Population":3653920784,"Female Population":3602569227,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2015,"Age":"0-14","Both Sexes Population":1860463578,"Male Population":962504434,"Female Population":897959144,"Percent Both Sexes":25.6,"Percent Male":26.3,"Percent Female":24.9,"Sex Ratio":107.2},{"Region":"World","Country":"Aggregated","Year":2015,"Age":"15-64","Both Sexes Population":4779884483,"Male Population":2416898840,"Female Population":2362985643,"Percent Both Sexes":65.9,"Percent Male":66.1,"Percent Female":65.6,"Sex Ratio":102.3},{"Region":"World","Country":"Aggregated","Year":2015,"Age":"65+","Both Sexes Population":616141950,"Male Population":274517510,"Female Population":341624440,"Percent Both Sexes":8.5,"Percent Male":7.5,"Percent Female":9.5,"Sex Ratio":80.4},{"Region":"World","Country":"Aggregated","Year":2016,"Age":"Total","Both Sexes Population":7334771614,"Male Population":3693280349,"Female Population":3641491265,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2016,"Age":"0-14","Both Sexes Population":1868438922,"Male Population":966405822,"Female Population":902033100,"Percent Both Sexes":25.5,"Percent Male":26.2,"Percent Female":24.8,"Sex Ratio":107.1},{"Region":"World","Country":"Aggregated","Year":2016,"Age":"15-64","Both Sexes Population":4830623741,"Male Population":2443497159,"Female Population":2387126582,"Percent Both Sexes":65.9,"Percent Male":66.2,"Percent Female":65.6,"Sex Ratio":102.4},{"Region":"World","Country":"Aggregated","Year":2016,"Age":"65+","Both Sexes Population":635708951,"Male Population":283377368,"Female Population":352331583,"Percent Both Sexes":8.7,"Percent Male":7.7,"Percent Female":9.7,"Sex Ratio":80.4},{"Region":"World","Country":"Aggregated","Year":2017,"Age":"Total","Both Sexes Population":7412778971,"Male Population":3732422544,"Female Population":3680356427,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2017,"Age":"0-14","Both Sexes Population":1876749474,"Male Population":970452250,"Female Population":906297224,"Percent Both Sexes":25.3,"Percent Male":26,"Percent Female":24.6,"Sex Ratio":107.1},{"Region":"World","Country":"Aggregated","Year":2017,"Age":"15-64","Both Sexes Population":4878438096,"Male Population":2468640794,"Female Population":2409797302,"Percent Both Sexes":65.8,"Percent Male":66.1,"Percent Female":65.5,"Sex Ratio":102.4},{"Region":"World","Country":"Aggregated","Year":2017,"Age":"65+","Both Sexes Population":657591401,"Male Population":293329500,"Female Population":364261901,"Percent Both Sexes":8.9,"Percent Male":7.9,"Percent Female":9.9,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2018,"Age":"Total","Both Sexes Population":7490427640,"Male Population":3771310557,"Female Population":3719117083,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2018,"Age":"0-14","Both Sexes Population":1884901719,"Male Population":974381709,"Female Population":910520010,"Percent Both Sexes":25.2,"Percent Male":25.8,"Percent Female":24.5,"Sex Ratio":107},{"Region":"World","Country":"Aggregated","Year":2018,"Age":"15-64","Both Sexes Population":4925337413,"Male Population":2493394363,"Female Population":2431943050,"Percent Both Sexes":65.8,"Percent Male":66.1,"Percent Female":65.4,"Sex Ratio":102.5},{"Region":"World","Country":"Aggregated","Year":2018,"Age":"65+","Both Sexes Population":680188508,"Male Population":303534485,"Female Population":376654023,"Percent Both Sexes":9.1,"Percent Male":8,"Percent Female":10.1,"Sex Ratio":80.6},{"Region":"World","Country":"Aggregated","Year":2019,"Age":"Total","Both Sexes Population":7567402977,"Male Population":3809785418,"Female Population":3757617559,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2019,"Age":"0-14","Both Sexes Population":1892402228,"Male Population":977936108,"Female Population":914466120,"Percent Both Sexes":25,"Percent Male":25.7,"Percent Female":24.3,"Sex Ratio":106.9},{"Region":"World","Country":"Aggregated","Year":2019,"Age":"15-64","Both Sexes Population":4970908006,"Male Population":2517524823,"Female Population":2453383183,"Percent Both Sexes":65.7,"Percent Male":66.1,"Percent Female":65.3,"Sex Ratio":102.6},{"Region":"World","Country":"Aggregated","Year":2019,"Age":"65+","Both Sexes Population":704092743,"Male Population":314324487,"Female Population":389768256,"Percent Both Sexes":9.3,"Percent Male":8.3,"Percent Female":10.4,"Sex Ratio":80.6},{"Region":"World","Country":"Aggregated","Year":2020,"Age":"Total","Both Sexes Population":7643402123,"Male Population":3847702175,"Female Population":3795699948,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.4},{"Region":"World","Country":"Aggregated","Year":2020,"Age":"0-14","Both Sexes Population":1899155782,"Male Population":981062194,"Female Population":918093588,"Percent Both Sexes":24.8,"Percent Male":25.5,"Percent Female":24.2,"Sex Ratio":106.9},{"Region":"World","Country":"Aggregated","Year":2020,"Age":"15-64","Both Sexes Population":5014616520,"Male Population":2540779058,"Female Population":2473837462,"Percent Both Sexes":65.6,"Percent Male":66,"Percent Female":65.2,"Sex Ratio":102.7},{"Region":"World","Country":"Aggregated","Year":2020,"Age":"65+","Both Sexes Population":729629821,"Male Population":325860923,"Female Population":403768898,"Percent Both Sexes":9.5,"Percent Male":8.5,"Percent Female":10.6,"Sex Ratio":80.7},{"Region":"World","Country":"Aggregated","Year":2021,"Age":"Total","Both Sexes Population":7718256830,"Male Population":3884971535,"Female Population":3833285295,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.3},{"Region":"World","Country":"Aggregated","Year":2021,"Age":"0-14","Both Sexes Population":1904978506,"Male Population":983663823,"Female Population":921314683,"Percent Both Sexes":24.7,"Percent Male":25.3,"Percent Female":24,"Sex Ratio":106.8},{"Region":"World","Country":"Aggregated","Year":2021,"Age":"15-64","Both Sexes Population":5059390977,"Male Population":2564672827,"Female Population":2494718150,"Percent Both Sexes":65.6,"Percent Male":66,"Percent Female":65.1,"Sex Ratio":102.8},{"Region":"World","Country":"Aggregated","Year":2021,"Age":"65+","Both Sexes Population":753887347,"Male Population":336634885,"Female Population":417252462,"Percent Both Sexes":9.8,"Percent Male":8.7,"Percent Female":10.9,"Sex Ratio":80.7},{"Region":"World","Country":"Aggregated","Year":2022,"Age":"Total","Both Sexes Population":7792021317,"Male Population":3921600380,"Female Population":3870420937,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.3},{"Region":"World","Country":"Aggregated","Year":2022,"Age":"0-14","Both Sexes Population":1909654905,"Male Population":985620463,"Female Population":924034442,"Percent Both Sexes":24.5,"Percent Male":25.1,"Percent Female":23.9,"Sex Ratio":106.7},{"Region":"World","Country":"Aggregated","Year":2022,"Age":"15-64","Both Sexes Population":5103107220,"Male Population":2588042724,"Female Population":2515064496,"Percent Both Sexes":65.5,"Percent Male":66,"Percent Female":65,"Sex Ratio":102.9},{"Region":"World","Country":"Aggregated","Year":2022,"Age":"65+","Both Sexes Population":779259192,"Male Population":347937193,"Female Population":431321999,"Percent Both Sexes":10,"Percent Male":8.9,"Percent Female":11.1,"Sex Ratio":80.7},{"Region":"World","Country":"Aggregated","Year":2023,"Age":"Total","Both Sexes Population":7864725370,"Male Population":3957588451,"Female Population":3907136919,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.3},{"Region":"World","Country":"Aggregated","Year":2023,"Age":"0-14","Both Sexes Population":1913225406,"Male Population":986958744,"Female Population":926266662,"Percent Both Sexes":24.3,"Percent Male":24.9,"Percent Female":23.7,"Sex Ratio":106.6},{"Region":"World","Country":"Aggregated","Year":2023,"Age":"15-64","Both Sexes Population":5145715457,"Male Population":2610784796,"Female Population":2534930661,"Percent Both Sexes":65.4,"Percent Male":66,"Percent Female":64.9,"Sex Ratio":103},{"Region":"World","Country":"Aggregated","Year":2023,"Age":"65+","Both Sexes Population":805784507,"Male Population":359844911,"Female Population":445939596,"Percent Both Sexes":10.2,"Percent Male":9.1,"Percent Female":11.4,"Sex Ratio":80.7},{"Region":"World","Country":"Aggregated","Year":2024,"Age":"Total","Both Sexes Population":7936271554,"Male Population":3992887649,"Female Population":3943383905,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.3},{"Region":"World","Country":"Aggregated","Year":2024,"Age":"0-14","Both Sexes Population":1916135671,"Male Population":987920308,"Female Population":928215363,"Percent Both Sexes":24.1,"Percent Male":24.7,"Percent Female":23.5,"Sex Ratio":106.4},{"Region":"World","Country":"Aggregated","Year":2024,"Age":"15-64","Both Sexes Population":5191332498,"Male Population":2635051426,"Female Population":2556281072,"Percent Both Sexes":65.4,"Percent Male":66,"Percent Female":64.8,"Sex Ratio":103.1},{"Region":"World","Country":"Aggregated","Year":2024,"Age":"65+","Both Sexes Population":828803385,"Male Population":369915915,"Female Population":458887470,"Percent Both Sexes":10.4,"Percent Male":9.3,"Percent Female":11.6,"Sex Ratio":80.6},{"Region":"World","Country":"Aggregated","Year":2025,"Age":"Total","Both Sexes Population":8006580553,"Male Population":4027458694,"Female Population":3979121859,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.2},{"Region":"World","Country":"Aggregated","Year":2025,"Age":"0-14","Both Sexes Population":1918459040,"Male Population":988539800,"Female Population":929919240,"Percent Both Sexes":24,"Percent Male":24.5,"Percent Female":23.4,"Sex Ratio":106.3},{"Region":"World","Country":"Aggregated","Year":2025,"Age":"15-64","Both Sexes Population":5235721968,"Male Population":2658675690,"Female Population":2577046278,"Percent Both Sexes":65.4,"Percent Male":66,"Percent Female":64.8,"Sex Ratio":103.2},{"Region":"World","Country":"Aggregated","Year":2025,"Age":"65+","Both Sexes Population":852376824,"Male Population":380236427,"Female Population":472140397,"Percent Both Sexes":10.6,"Percent Male":9.4,"Percent Female":11.9,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2026,"Age":"Total","Both Sexes Population":8075716000,"Male Population":4061352955,"Female Population":4014363045,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.2},{"Region":"World","Country":"Aggregated","Year":2026,"Age":"0-14","Both Sexes Population":1920135783,"Male Population":988800324,"Female Population":931335459,"Percent Both Sexes":23.8,"Percent Male":24.3,"Percent Female":23.2,"Sex Ratio":106.2},{"Region":"World","Country":"Aggregated","Year":2026,"Age":"15-64","Both Sexes Population":5281741663,"Male Population":2683172469,"Female Population":2598569194,"Percent Both Sexes":65.4,"Percent Male":66.1,"Percent Female":64.7,"Sex Ratio":103.3},{"Region":"World","Country":"Aggregated","Year":2026,"Age":"65+","Both Sexes Population":873838554,"Male Population":389380162,"Female Population":484458392,"Percent Both Sexes":10.8,"Percent Male":9.6,"Percent Female":12.1,"Sex Ratio":80.4},{"Region":"World","Country":"Aggregated","Year":2027,"Age":"Total","Both Sexes Population":8143729466,"Male Population":4094613125,"Female Population":4049116341,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.1},{"Region":"World","Country":"Aggregated","Year":2027,"Age":"0-14","Both Sexes Population":1921055741,"Male Population":988677963,"Female Population":932377778,"Percent Both Sexes":23.6,"Percent Male":24.1,"Percent Female":23,"Sex Ratio":106},{"Region":"World","Country":"Aggregated","Year":2027,"Age":"15-64","Both Sexes Population":5323867670,"Male Population":2705647499,"Female Population":2618220171,"Percent Both Sexes":65.4,"Percent Male":66.1,"Percent Female":64.7,"Sex Ratio":103.3},{"Region":"World","Country":"Aggregated","Year":2027,"Age":"65+","Both Sexes Population":898806055,"Male Population":400287663,"Female Population":498518392,"Percent Both Sexes":11,"Percent Male":9.8,"Percent Female":12.3,"Sex Ratio":80.3},{"Region":"World","Country":"Aggregated","Year":2028,"Age":"Total","Both Sexes Population":8210559895,"Male Population":4127207443,"Female Population":4083352452,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101.1},{"Region":"World","Country":"Aggregated","Year":2028,"Age":"0-14","Both Sexes Population":1921320500,"Male Population":988223928,"Female Population":933096572,"Percent Both Sexes":23.4,"Percent Male":23.9,"Percent Female":22.9,"Sex Ratio":105.9},{"Region":"World","Country":"Aggregated","Year":2028,"Age":"15-64","Both Sexes Population":5355950734,"Male Population":2723020071,"Female Population":2632930663,"Percent Both Sexes":65.2,"Percent Male":66,"Percent Female":64.5,"Sex Ratio":103.4},{"Region":"World","Country":"Aggregated","Year":2028,"Age":"65+","Both Sexes Population":933252586,"Male Population":415955138,"Female Population":517297448,"Percent Both Sexes":11.4,"Percent Male":10.1,"Percent Female":12.7,"Sex Ratio":80.4},{"Region":"World","Country":"Aggregated","Year":2029,"Age":"Total","Both Sexes Population":8276190519,"Male Population":4159129959,"Female Population":4117060560,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101},{"Region":"World","Country":"Aggregated","Year":2029,"Age":"0-14","Both Sexes Population":1920983422,"Male Population":987469055,"Female Population":933514367,"Percent Both Sexes":23.2,"Percent Male":23.7,"Percent Female":22.7,"Sex Ratio":105.8},{"Region":"World","Country":"Aggregated","Year":2029,"Age":"15-64","Both Sexes Population":5389053369,"Male Population":2740912169,"Female Population":2648141200,"Percent Both Sexes":65.1,"Percent Male":65.9,"Percent Female":64.3,"Sex Ratio":103.5},{"Region":"World","Country":"Aggregated","Year":2029,"Age":"65+","Both Sexes Population":966153728,"Male Population":430748735,"Female Population":535404993,"Percent Both Sexes":11.7,"Percent Male":10.4,"Percent Female":13,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2030,"Age":"Total","Both Sexes Population":8340606590,"Male Population":4190376720,"Female Population":4150229870,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":101},{"Region":"World","Country":"Aggregated","Year":2030,"Age":"0-14","Both Sexes Population":1919946650,"Male Population":986361865,"Female Population":933584785,"Percent Both Sexes":23,"Percent Male":23.5,"Percent Female":22.5,"Sex Ratio":105.7},{"Region":"World","Country":"Aggregated","Year":2030,"Age":"15-64","Both Sexes Population":5422608614,"Male Population":2758948186,"Female Population":2663660428,"Percent Both Sexes":65,"Percent Male":65.8,"Percent Female":64.2,"Sex Ratio":103.6},{"Region":"World","Country":"Aggregated","Year":2030,"Age":"65+","Both Sexes Population":998051326,"Male Population":445066669,"Female Population":552984657,"Percent Both Sexes":12,"Percent Male":10.6,"Percent Female":13.3,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2031,"Age":"Total","Both Sexes Population":8403880343,"Male Population":4220984684,"Female Population":4182895659,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":100.9},{"Region":"World","Country":"Aggregated","Year":2031,"Age":"0-14","Both Sexes Population":1918437104,"Male Population":985024348,"Female Population":933412756,"Percent Both Sexes":22.8,"Percent Male":23.3,"Percent Female":22.3,"Sex Ratio":105.5},{"Region":"World","Country":"Aggregated","Year":2031,"Age":"15-64","Both Sexes Population":5455751431,"Male Population":2776682675,"Female Population":2679068756,"Percent Both Sexes":64.9,"Percent Male":65.8,"Percent Female":64,"Sex Ratio":103.6},{"Region":"World","Country":"Aggregated","Year":2031,"Age":"65+","Both Sexes Population":1029691808,"Male Population":459277661,"Female Population":570414147,"Percent Both Sexes":12.3,"Percent Male":10.9,"Percent Female":13.6,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2032,"Age":"Total","Both Sexes Population":8466094022,"Male Population":4250995647,"Female Population":4215098375,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":100.9},{"Region":"World","Country":"Aggregated","Year":2032,"Age":"0-14","Both Sexes Population":1916701648,"Male Population":983588364,"Female Population":933113284,"Percent Both Sexes":22.6,"Percent Male":23.1,"Percent Female":22.1,"Sex Ratio":105.4},{"Region":"World","Country":"Aggregated","Year":2032,"Age":"15-64","Both Sexes Population":5489972927,"Male Population":2794825488,"Female Population":2695147439,"Percent Both Sexes":64.8,"Percent Male":65.7,"Percent Female":63.9,"Sex Ratio":103.7},{"Region":"World","Country":"Aggregated","Year":2032,"Age":"65+","Both Sexes Population":1059419447,"Male Population":472581795,"Female Population":586837652,"Percent Both Sexes":12.5,"Percent Male":11.1,"Percent Female":13.9,"Sex Ratio":80.5},{"Region":"World","Country":"Aggregated","Year":2033,"Age":"Total","Both Sexes Population":8527246205,"Male Population":4280411563,"Female Population":4246834642,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":100.8},{"Region":"World","Country":"Aggregated","Year":2033,"Age":"0-14","Both Sexes Population":1914921585,"Male Population":982148765,"Female Population":932772820,"Percent Both Sexes":22.5,"Percent Male":22.9,"Percent Female":22,"Sex Ratio":105.3},{"Region":"World","Country":"Aggregated","Year":2033,"Age":"15-64","Both Sexes Population":5521671011,"Male Population":2811561016,"Female Population":2710109995,"Percent Both Sexes":64.8,"Percent Male":65.7,"Percent Female":63.8,"Sex Ratio":103.7},{"Region":"World","Country":"Aggregated","Year":2033,"Age":"65+","Both Sexes Population":1090653609,"Male Population":486701782,"Female Population":603951827,"Percent Both Sexes":12.8,"Percent Male":11.4,"Percent Female":14.2,"Sex Ratio":80.6},{"Region":"World","Country":"Aggregated","Year":2034,"Age":"Total","Both Sexes Population":8587325154,"Male Population":4309229068,"Female Population":4278096086,"Percent Both Sexes":100,"Percent Male":100,"Percent Female":100,"Sex Ratio":100.7},{"Region":"World","Country":"Aggregated","Year":2034,"Age":"0-14","Both Sexes Population":1913252246,"Male Population":980786741,"Female Population":932465505,"Percent Both Sexes":22.3,"Percent Male":22.8,"Percent Female":21.8,"Sex Ratio":105.2},{"Region":"World","Country":"Aggregated","Year":2034,"Age":"15-64","Both Sexes Population":5549813190,"Male Population":2826523773,"Female Population":2723289417,"Percent Both Sexes":64.6,"Percent Male":65.6,"Percent Female":63.7,"Sex Ratio":103.8},{"Region":"World","Country":"Aggregated","Year":2034,"Age":"65+","Both Sexes Population":1124259718,"Male Population":501918554,"Female Population":622341164,"Percent Both Sexes":13.1,"Percent Male":11.6,"Percent Female":14.5,"Sex Ratio":80.7}] -------------------------------------------------------------------------------- /src/assets/images/d3-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topheman/d3-react-experiments/909e90c83973284e1cb9659ca2b6d5ac2e56df49/src/assets/images/d3-logo.png -------------------------------------------------------------------------------- /src/assets/images/github-retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topheman/d3-react-experiments/909e90c83973284e1cb9659ca2b6d5ac2e56df49/src/assets/images/github-retina.png -------------------------------------------------------------------------------- /src/assets/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topheman/d3-react-experiments/909e90c83973284e1cb9659ca2b6d5ac2e56df49/src/assets/images/github.png -------------------------------------------------------------------------------- /src/assets/images/react-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/images/twitter-retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topheman/d3-react-experiments/909e90c83973284e1cb9659ca2b6d5ac2e56df49/src/assets/images/twitter-retina.png -------------------------------------------------------------------------------- /src/assets/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topheman/d3-react-experiments/909e90c83973284e1cb9659ca2b6d5ac2e56df49/src/assets/images/twitter.png -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | import 'array-includes/shim'; 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import { AppContainer } from 'react-hot-loader'; // will go away in 'production' mode 6 | import { hashHistory } from 'react-router'; 7 | 8 | import routes from './routes'; 9 | import Root from './components/Root/Root'; 10 | 11 | global.Promise = global.Promise || require('es6-promise').Promise; 12 | 13 | const renderApp = (appRoutes, history) => { 14 | render( 15 | 16 | 17 | , 18 | document.getElementById('app-container') 19 | ); 20 | }; 21 | 22 | renderApp(routes, hashHistory); 23 | 24 | if (module.hot) { 25 | module.hot.accept('./routes', () => { 26 | const newRoutes = require('./routes').default; 27 | renderApp(newRoutes, hashHistory); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/CountriesChartPanel/CountriesChartPanel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { ColoredMultiSelect } from '../Select'; 4 | import ViewSourceOnGithub from '../ViewSourceOnGithub/ViewSourceOnGithub'; 5 | 6 | export default class CountriesChartPanel extends React.Component { 7 | 8 | static propTypes = { 9 | data: React.PropTypes.array.isRequired, 10 | sourcesOnGithub: React.PropTypes.oneOfType([ 11 | React.PropTypes.string, 12 | React.PropTypes.object 13 | ]).isRequired, 14 | title: React.PropTypes.string.isRequired, 15 | props: React.PropTypes.object, // props that will be injected as props into the component passed here 16 | prepareData: React.PropTypes.func.isRequired, // callback that will process the data (data, countryList) => {"France": [{x: 50, y: 1950}]} 17 | component: React.PropTypes.func.isRequired, 18 | panelSubText: React.PropTypes.oneOfType([ 19 | React.PropTypes.node, 20 | React.PropTypes.string 21 | ]), 22 | defaultSelectedCountries: React.PropTypes.array, 23 | // injected by injectWindowInfos 24 | windowWidth: React.PropTypes.number 25 | } 26 | 27 | constructor({ defaultSelectedCountries = ['Algeria', 'Ethiopia', 'France', 'Germany', 'India'] }) { 28 | super(); 29 | this.state = { 30 | selectedCountries: defaultSelectedCountries.map(country => ({ label: country, value: country })) 31 | }; 32 | } 33 | 34 | render() { 35 | 36 | const { title, data, sourcesOnGithub, component: Chart, prepareData, props = {}, windowWidth, panelSubText } = this.props; 37 | 38 | const countryList = data.reduce((acc, cur) => { 39 | if (acc.indexOf(cur.Country) < 0) { 40 | acc.push(cur.Country); 41 | } 42 | return acc; 43 | }, []).sort((a, b) => a > b ? 1 : -1); 44 | 45 | const { selectedCountries } = this.state; 46 | 47 | // prepare data 48 | const chartData = prepareData(data, selectedCountries.map(country => country.value || country)); 49 | 50 | return ( 51 |
this.rootNode = node}> 52 |
{title}{selectedCountries.length > 0 ? from {chartData.minX} to {chartData.maxX} : null}
53 | 54 |
55 |

Add / Remove any country from the list.

56 | ({ value: country, label: country }))} 59 | onChange={(currentCountries) => { 60 | this.setState({ 61 | ...this.state, 62 | selectedCountries: currentCountries 63 | }); 64 | }} 65 | /> 66 | 700 ? 630 : windowWidth - 70} 69 | {...chartData} 70 | /> 71 |
72 | {panelSubText} 73 |
74 |
75 |
76 | 77 | ); 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TwitterButton from './../TwitterButton/TwitterButton'; 4 | 5 | const Footer = () => ( 6 |
7 |

8 | ©2015 - {(new Date()).getFullYear()} labs.topheman.com - Christophe Rosset
9 | 10 |

11 |
12 | ); 13 | 14 | export default Footer; 15 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { IndexLink, Link } from 'react-router'; 3 | 4 | export default class Header extends React.Component { 5 | 6 | static propTypes = { 7 | title: React.PropTypes.string 8 | } 9 | 10 | constructor(props) { 11 | 12 | super(props); 13 | 14 | // init context bindings - due to diff between React.createClass and ES6 class 15 | this.handleClick = this.handleClick.bind(this); 16 | 17 | // init state 18 | this.state = { 19 | collapsed: true 20 | }; 21 | 22 | } 23 | handleClick() { 24 | const { collapsed } = this.state; 25 | this.setState({ collapsed: !collapsed }); 26 | } 27 | render() { 28 | 29 | const collapsedMenuClassName = `collapse navbar-collapse${this.state.collapsed === true ? '' : ' in'}`; 30 | const { title } = this.props; 31 | 32 | return ( 33 |
34 | 55 | 56 | 70 |
71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/components/LifeExpectancy/LifeExpectancy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This component is meant to be wrapped by a HOC 3 | * that way, I can produce many pages with all this managing system of data loading 4 | * the Select algo being also encapsulated in this LifeExpectancy HOC 5 | */ 6 | 7 | import React from 'react'; 8 | 9 | import CountriesChartPanel from '../../components/CountriesChartPanel/CountriesChartPanel'; 10 | import { asyncLoadLifeExpectancy } from '../../resources/loaders'; 11 | import { injectWindowInfos } from '../../components/WindowInfos'; 12 | 13 | // decorating this component so that it will receive windowWidth, windowHeight in its props (passed down from the WindowInfos.Provider) 14 | const WindowAwareCountriesChartPanel = injectWindowInfos()(CountriesChartPanel); 15 | 16 | class LifeExpectancy extends React.Component { 17 | 18 | static propTypes = { 19 | title: React.PropTypes.oneOfType([ 20 | React.PropTypes.node, 21 | React.PropTypes.string 22 | ]).isRequired, 23 | prepareData: React.PropTypes.func.isRequired, 24 | sourcesOnGithub: React.PropTypes.oneOfType([ 25 | React.PropTypes.object, 26 | React.PropTypes.string 27 | ]).isRequired, 28 | component: React.PropTypes.func.isRequired, 29 | panelSubText: React.PropTypes.oneOfType([ 30 | React.PropTypes.node, 31 | React.PropTypes.string 32 | ]) 33 | } 34 | 35 | constructor() { 36 | super(); 37 | this.state = { 38 | ready: false, 39 | error: false 40 | }; 41 | this.loadData = this.loadData.bind(this); 42 | } 43 | 44 | componentDidMount() { 45 | this.loadData(); 46 | } 47 | 48 | loadData() { 49 | this.setState({ 50 | ...this.state, 51 | ready: false, 52 | error: false 53 | }); 54 | asyncLoadLifeExpectancy() 55 | .then(data => { 56 | this.setState({ 57 | ...this.state, 58 | ready: true, 59 | data 60 | }); 61 | }) 62 | .catch(e => { 63 | this.setState({ 64 | ...this.state, 65 | error: true, 66 | data: null 67 | }); 68 | console.error('An error occured while loading data', e); 69 | }); 70 | } 71 | 72 | render() { 73 | const { ready, error, data } = this.state; 74 | const { title, prepareData, sourcesOnGithub, component, panelSubText } = this.props; 75 | return ( 76 |
77 |

{title}

78 | {!ready && !error &&

Loading ...

} 79 | {!ready && error &&
80 | 81 | {' '}An error occured while loading data - Click here to retry 82 |
} 83 | {ready && !error && } 92 |

Data comes from ourworldindata.org

93 |
94 | ); 95 | } 96 | 97 | } 98 | 99 | export default LifeExpectancy; 100 | -------------------------------------------------------------------------------- /src/components/Navigator/injectNavigator.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-multi-comp, react/prop-types */ 2 | 3 | import React from 'react'; 4 | import { Link } from 'react-router'; 5 | import { Link as ScrollLink, Element as ScrollElement, animateScroll } from 'react-scroll'; 6 | 7 | import { getDisplayName } from '../../utils/helpers'; 8 | 9 | export const defaultLinks = [ 10 | '/d3/transition-multi-line-chart', 11 | '/d3/static-multi-line-chart', 12 | '/d3/react-faux-dom/static-multi-line-chart', 13 | '/recharts/transition-multi-line-chart', 14 | '/victory/world-population-by-age-range', 15 | '/victory/transition-multi-line-chart', 16 | '/victory/count-npm-downloads', 17 | '/d3act/bar-chart', 18 | '/d3act/mixed-chart' 19 | ]; 20 | 21 | const getPrevNextLinks = (location, links = defaultLinks) => { 22 | const currentPath = location.pathname; 23 | const prevNextLinks = {}; 24 | // 0 or 1 -> dont display pager 25 | if (links.length < 2) { 26 | return prevNextLinks; 27 | } 28 | // only 2 -> always loop on next 29 | if (links.length === 2) { 30 | prevNextLinks.next = links.filter(link => link !== currentPath)[0]; 31 | return prevNextLinks; 32 | } 33 | // more links 34 | if (links.indexOf(currentPath) === 0) { 35 | prevNextLinks.prev = links[links.length - 1]; 36 | prevNextLinks.next = links[1]; 37 | } 38 | else if (links.indexOf(currentPath) === links.length - 1) { 39 | prevNextLinks.prev = links[links.length - 2]; 40 | prevNextLinks.next = links[0]; 41 | } 42 | else { 43 | const currentPathIndex = links.indexOf(currentPath); 44 | prevNextLinks.prev = links[currentPathIndex - 1]; 45 | prevNextLinks.next = links[currentPathIndex + 1]; 46 | } 47 | return prevNextLinks; 48 | }; 49 | 50 | const prevNext = ({ prev, next, middle }) => ( 51 | 58 | ); 59 | 60 | const navigator = (links) => WrappedComponent => { 61 | const Navigator = ({ location, ...props }) => { 62 | const { prev, next } = getPrevNextLinks(location, links); 63 | if (prev || next) { 64 | return ( 65 |
66 | {prevNext({ prev, next, middle: Description })} 67 | 68 | 69 | {prevNext({ prev, next, middle: animateScroll.scrollToTop()} style={{ cursor: 'pointer', fontSize: '80%' }} role="button" tabIndex={0}>Back to top })} 70 | 71 |
72 | ); 73 | } 74 | return (); 75 | }; 76 | Navigator.displayName = `Navigator(${getDisplayName(WrappedComponent)})`; 77 | Navigator.contextTypes = { 78 | location: React.PropTypes.object.isRequired 79 | }; 80 | Navigator.propTypes = { 81 | links: React.PropTypes.arrayOf(React.PropTypes.string) 82 | }; 83 | return Navigator; 84 | }; 85 | 86 | export default navigator; 87 | -------------------------------------------------------------------------------- /src/components/ReactFauxDom/StaticMultiLineChart/StaticMultiLineChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ColorHash from 'color-hash'; 4 | 5 | import { scaleLinear } from 'd3-scale'; 6 | import { line } from 'd3-shape'; 7 | import { select } from 'd3-selection'; 8 | import { axisBottom, axisLeft } from 'd3-axis'; 9 | import ReactFauxDOM from 'react-faux-dom'; 10 | 11 | const colorHash = new ColorHash(); 12 | 13 | export default class StaticMultiLineChart extends React.Component { 14 | 15 | static propTypes = { 16 | margin: React.PropTypes.object, 17 | width: React.PropTypes.number, 18 | height: React.PropTypes.number, 19 | data: React.PropTypes.object.isRequired, 20 | minX: React.PropTypes.number, 21 | maxX: React.PropTypes.number, 22 | minY: React.PropTypes.number, 23 | maxY: React.PropTypes.number 24 | } 25 | 26 | static defaultProps = { 27 | margin: { 28 | top: 20, 29 | right: 20, 30 | bottom: 30, 31 | left: 50 32 | }, 33 | width: 700, 34 | height: 400 35 | } 36 | 37 | drawLineChart() { 38 | const { margin, width: widthIncludingMargins, height: heightIncludingMargins, data, minX, maxX, minY, maxY } = this.props; 39 | console.log('margin', margin, minX, maxX, minY, maxY, 'data', data); 40 | 41 | // create a fake DOMNode thanks to react-faux-dom, which will trick d3 apis 42 | // that way, d3 doesn't access directly to the DOM (no mutations) - just call .toReact() on this node object 43 | const rootNode = ReactFauxDOM.createElement('svg'); 44 | 45 | // we are drawing the chart just like in regular d3 - querying the DOM, adding element 46 | // we don't care about jsx, we redraw at each change 47 | // that way, we could copy/paste any example from bl.ocks.org ... 48 | // this isn't the best approach though 49 | const width = widthIncludingMargins - margin.left - margin.right; 50 | const height = heightIncludingMargins - margin.top - margin.bottom; 51 | 52 | console.log('width', width, 'height', height); 53 | 54 | // set the ranges 55 | const x = scaleLinear().range([0, width]); 56 | const y = scaleLinear().range([height, 0]); 57 | 58 | // define line getter 59 | const valueLine = line() 60 | .x(d => x(d.x)) 61 | .y(d => y(d.y)); 62 | 63 | // append the svg object to the body of the page 64 | // appends a 'group' element to 'svg' 65 | // moves the 'group' element to the top left margin 66 | const svg = select(rootNode) 67 | .attr('width', width + margin.left + margin.right) 68 | .attr('height', height + margin.top + margin.bottom) 69 | .append('g') 70 | .attr('transform', 71 | `translate(${margin.left},${margin.top})`); 72 | 73 | // Scale the range of the data 74 | x.domain([minX, maxX]); 75 | y.domain([0, maxY]); 76 | 77 | Object.keys(data).forEach(countryName => { 78 | svg.append('path') 79 | .data([data[countryName]]) 80 | .style('fill', 'none') 81 | .style('stroke-width', '2px') 82 | .style('stroke', colorHash.hex(countryName)) 83 | .attr('d', valueLine); 84 | }); 85 | 86 | // Add the X Axis 87 | svg.append('g') 88 | .attr('transform', `translate(0,${height})`) 89 | .call(axisBottom(x).ticks(width > 500 ? Math.floor(width / 80) : 4)); // prevent from having too much ticks on small screens 90 | 91 | // Add the Y Axis 92 | svg.append('g') 93 | .call(axisLeft(y)); 94 | 95 | return rootNode; 96 | } 97 | 98 | render() { 99 | return this.drawLineChart().toReact(); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/components/ReactFauxDom/__tests__/StaticMultiLineChart.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import toJson from 'enzyme-to-json'; 4 | 5 | import StaticMultiLineChart from '../StaticMultiLineChart/StaticMultiLineChart'; 6 | import { mockDataLifeExpectancy } from '../../../test/jestHelpers'; 7 | 8 | describe('components/ReactFauxDom/StaticMultiLineChart', () => { 9 | it('should render correctly [shallow]', () => { 10 | const wrapper = shallow( 11 | 14 | ); 15 | expect(toJson(wrapper)).toMatchSnapshot(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prefer-stateless-function */ 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import { Router } from 'react-router'; 5 | 6 | /** 7 | * So that react-hot-loader works, you need a class component to hold the root of your app. 8 | * (This is the way react-hot-loader works, it will wrap the class, add proxy method to update them on the fly) 9 | */ 10 | export default class Root extends Component { 11 | static propTypes = { 12 | history: PropTypes.object, 13 | routes: PropTypes.object 14 | } 15 | render() { 16 | return ( 17 | 18 | {this.props.routes} 19 | 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Select/ColoredMultiSelect.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-multi-comp */ 2 | 3 | import React from 'react'; 4 | import ColorHash from 'color-hash'; 5 | 6 | import { Select as OriginalSelect } from './Select'; 7 | 8 | const colorHash = new ColorHash(); 9 | 10 | const valueRenderer = (option) => (× {option.label}); 17 | 18 | /** 19 | * Works just like react-select 20 | * You can pass as props colorLabel = true to have deterministic colored labels (using color-hash) 21 | */ 22 | export const ColoredMultiSelect = (props) => ; 23 | -------------------------------------------------------------------------------- /src/components/Select/Select.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-multi-comp */ 2 | 3 | import React from 'react'; 4 | import ReactSelect from 'react-select'; 5 | 6 | /** 7 | * Works just like react-select 8 | * You can pass as props colorLabel = true to have deterministic colored labels (using color-hash) 9 | */ 10 | export const Select = (props) => ; 11 | -------------------------------------------------------------------------------- /src/components/Select/index.js: -------------------------------------------------------------------------------- 1 | export { Select } from './Select'; 2 | export { ColoredMultiSelect } from './ColoredMultiSelect'; 3 | -------------------------------------------------------------------------------- /src/components/Select/style/ColoredMultiSelect.scss: -------------------------------------------------------------------------------- 1 | .Select.color-label .Select-value { 2 | position: relative; 3 | & .Select-value-icon { 4 | position: absolute; 5 | background: transparent; 6 | color: transparent; 7 | width: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Slider/Slider.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/label-has-for */ 2 | 3 | import React from 'react'; 4 | 5 | export default class Slider extends React.Component { 6 | 7 | static propTypes = { 8 | min: React.PropTypes.number, 9 | max: React.PropTypes.number, 10 | step: React.PropTypes.number, 11 | defaultValue: React.PropTypes.number, 12 | label: React.PropTypes.string, 13 | update: React.PropTypes.func.isRequired, 14 | showPlayButton: React.PropTypes.bool, 15 | playing: React.PropTypes.bool, 16 | playingInterval: React.PropTypes.number 17 | } 18 | 19 | static defaultProps = { 20 | min: 0, 21 | max: 200, 22 | step: 5, 23 | defaultValue: 0, 24 | label: 'Value: %value', 25 | showPlayButton: false, 26 | playing: false, 27 | playingInterval: 500 28 | } 29 | 30 | constructor(props) { 31 | super(props); 32 | this.state = { 33 | value: props.defaultValue, 34 | playing: props.playing 35 | }; 36 | this.updateValue = this.updateValue.bind(this); 37 | this.handleChange = this.handleChange.bind(this); 38 | this.run = this.run.bind(this); 39 | this.play = this.play.bind(this); 40 | this.pause = this.pause.bind(this); 41 | this.handleClickPlay = this.handleClickPlay.bind(this); 42 | } 43 | 44 | componentDidMount() { 45 | this.run(); 46 | } 47 | 48 | componentWillUnmount() { 49 | clearTimeout(this.timer); 50 | } 51 | 52 | updateValue(value) { 53 | const { update } = this.props; 54 | this.setState({ 55 | value 56 | }); 57 | update(value); 58 | } 59 | 60 | handleChange(e) { 61 | if (this.state.playing) { 62 | return; 63 | } 64 | const value = parseInt(e.target.value, 10); 65 | this.updateValue(value); 66 | } 67 | 68 | run() { 69 | if (this.state.playing) { 70 | this.play(); 71 | } 72 | else { 73 | this.pause(); 74 | } 75 | } 76 | 77 | play() { 78 | const { value } = this.state; 79 | const { min, max, step, playingInterval } = this.props; 80 | let next; 81 | if ((value + step) <= max) { 82 | next = value + step; 83 | } 84 | else { 85 | next = min; 86 | } 87 | this.updateValue(next); 88 | this.timer = setTimeout(this.play, playingInterval); 89 | } 90 | 91 | pause() { 92 | clearTimeout(this.timer); 93 | } 94 | 95 | handleClickPlay() { 96 | this.setState({ 97 | playing: !this.state.playing 98 | }, () => { 99 | this.run(); 100 | }); 101 | } 102 | 103 | render() { 104 | const { min, max, step, label, showPlayButton } = this.props; 105 | return ( 106 |
107 | {label !== null ? : null} 108 | 122 | {showPlayButton ? : null} 123 |
124 | ); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/components/TwitterButton/TwitterButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | /** 4 | * This component renders directly the iframe of twitter without running external script 5 | * to avoid messing up with react's internal DOM and break react hot loader 6 | * 7 | * @todo make a more generic version 8 | * @note : Tweet 9 | */ 10 | export default function TwitterButton() { 11 | return ( 12 |