From e6ea3c73261362ee6c61cf1959bf2b1ad4bceab1 Mon Sep 17 00:00:00 2001 From: James Greenaway Date: Mon, 16 May 2016 11:19:00 +0100 Subject: [PATCH] Big headway --- .eslintignore | 6 +- .eslintrc | 7 +- README.md | 6 + bin/compile.js | 30 ++-- bin/server.js | 18 +-- blueprints/blueprint/index.js | 16 +- blueprints/duck/index.js | 8 +- blueprints/dumb/index.js | 8 +- blueprints/form/index.js | 8 +- blueprints/layout/index.js | 8 +- blueprints/smart/index.js | 16 +- blueprints/view/index.js | 8 +- build/karma.conf.js | 50 +++--- build/webpack-compiler.js | 42 +++--- build/webpack.config.js | 142 +++++++++--------- config/environments.js | 14 +- config/index.js | 60 ++++---- package.json | 9 +- server/lib/apply-express-middleware.js | 16 +- server/main.js | 52 +++---- server/middleware/webpack-dev.js | 30 ++-- server/middleware/webpack-hmr.js | 20 +-- src/components/Counter/index.js | 3 - src/components/DisplayPicker/DisplayPicker.js | 38 +++++ .../DisplayPicker/DisplayPickerEmbedCode.js | 9 ++ .../DisplayPicker/DisplayPickerNavigation.js | 32 ++++ .../DisplayPicker/DisplayPickerStage.js | 20 +++ src/components/DisplayPicker/index.js | 2 + src/components/FastInput/FastInput.js | 39 +++++ src/components/FastInput/index.js | 2 + src/components/Header/Header.js | 28 ++-- src/components/Header/index.js | 4 +- src/components/PushButton/PushButton.js | 26 ++++ src/components/PushButton/index.js | 2 + .../{Counter => _Counter}/Counter.js | 16 +- .../{Counter => _Counter}/Counter.scss | 0 src/components/_Counter/index.js | 3 + src/components/_Header/Header.js | 18 +++ src/components/_Header/Header.scss | 4 + src/components/_Header/index.js | 3 + src/containers/AppContainer.js | 40 +++-- src/index.html | 2 +- src/layouts/CoreLayout/CoreLayout.js | 18 +-- src/layouts/CoreLayout/index.js | 4 +- src/main.js | 54 +++---- src/routes/Home/components/HomeView.js | 24 +-- src/routes/Home/components/HomeView.scss | 5 - .../Home/containers/DisplayPickerContainer.js | 20 +++ src/routes/Home/index.js | 7 +- src/routes/Home/modules/display.js | 51 +++++++ .../containers/CounterContainer.js | 16 +- src/routes/{Counter => _Counter}/index.js | 18 +-- .../{Counter => _Counter}/modules/counter.js | 40 +++-- src/routes/{Home => _Home}/assets/Duck.jpg | Bin src/routes/_Home/components/HomeView.js | 16 ++ src/routes/_Home/components/HomeView.scss | 5 + src/routes/_Home/index.js | 6 + src/routes/_index.js | 36 +++++ src/routes/index.js | 36 +---- src/store/createStore.js | 31 ++-- src/store/reducers.js | 25 +-- tests/components/DisplayPicker.spec.js | 8 + tests/components/FastInput.spec.js | 8 + tests/components/PushButton.spec.js | 8 + .../containers/DisplayPickerContainer.spec.js | 5 + tests/routes/Home/modules/display.spec.js | 9 ++ tests/test-bundler.js | 34 ++--- 67 files changed, 845 insertions(+), 504 deletions(-) delete mode 100644 src/components/Counter/index.js create mode 100644 src/components/DisplayPicker/DisplayPicker.js create mode 100644 src/components/DisplayPicker/DisplayPickerEmbedCode.js create mode 100644 src/components/DisplayPicker/DisplayPickerNavigation.js create mode 100644 src/components/DisplayPicker/DisplayPickerStage.js create mode 100644 src/components/DisplayPicker/index.js create mode 100644 src/components/FastInput/FastInput.js create mode 100644 src/components/FastInput/index.js create mode 100644 src/components/PushButton/PushButton.js create mode 100644 src/components/PushButton/index.js rename src/components/{Counter => _Counter}/Counter.js (61%) rename src/components/{Counter => _Counter}/Counter.scss (100%) create mode 100644 src/components/_Counter/index.js create mode 100644 src/components/_Header/Header.js create mode 100644 src/components/_Header/Header.scss create mode 100644 src/components/_Header/index.js create mode 100644 src/routes/Home/containers/DisplayPickerContainer.js create mode 100644 src/routes/Home/modules/display.js rename src/routes/{Counter => _Counter}/containers/CounterContainer.js (86%) rename src/routes/{Counter => _Counter}/index.js (68%) rename src/routes/{Counter => _Counter}/modules/counter.js (64%) rename src/routes/{Home => _Home}/assets/Duck.jpg (100%) create mode 100644 src/routes/_Home/components/HomeView.js create mode 100644 src/routes/_Home/components/HomeView.scss create mode 100644 src/routes/_Home/index.js create mode 100644 src/routes/_index.js create mode 100644 tests/components/DisplayPicker.spec.js create mode 100644 tests/components/FastInput.spec.js create mode 100644 tests/components/PushButton.spec.js create mode 100644 tests/containers/DisplayPickerContainer.spec.js create mode 100644 tests/routes/Home/modules/display.spec.js diff --git a/.eslintignore b/.eslintignore index f517d4c..77c5fb7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,10 @@ -blueprints/**/files/** +bin +blueprints coverage/** node_modules/** dist/** *.spec.js src/index.html +config +build +server diff --git a/.eslintrc b/.eslintrc index 4a6fe9c..f5c335c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,8 +1,7 @@ { - "parser" : "babel-eslint", + "parser": "babel-eslint", "extends" : [ - "standard", - "standard-react" + "airbnb" ], "env" : { "browser" : true @@ -15,7 +14,5 @@ "__BASENAME__" : false }, "rules": { - "semi" : [2, "never"], - "max-len": [2, 120, 2] } } diff --git a/README.md b/README.md index c5ca6cd..6f889c5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# Climb Social Displayer + +A standalone application which demonstrates different Climb Social views and helps users embed them in their own web pages. + +--- + # React Redux Starter Kit [![Join the chat at https://gitter.im/davezuko/react-redux-starter-kit](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/davezuko/react-redux-starter-kit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/bin/compile.js b/bin/compile.js index 76126d4..838b050 100644 --- a/bin/compile.js +++ b/bin/compile.js @@ -1,24 +1,24 @@ -import fs from 'fs-extra' -import _debug from 'debug' -import webpackCompiler from '../build/webpack-compiler' -import webpackConfig from '../build/webpack.config' -import config from '../config' +import fs from 'fs-extra'; +import _debug from 'debug'; +import webpackCompiler from '../build/webpack-compiler'; +import webpackConfig from '../build/webpack.config'; +import config from '../config'; -const debug = _debug('app:bin:compile') +const debug = _debug('app:bin:compile'); const paths = config.utils_paths ;(async function () { try { - debug('Run compiler') - const stats = await webpackCompiler(webpackConfig) + debug('Run compiler'); + const stats = await webpackCompiler(webpackConfig); if (stats.warnings.length && config.compiler_fail_on_warning) { - debug('Config set to fail on warning, exiting with status code "1".') - process.exit(1) + debug('Config set to fail on warning, exiting with status code "1".'); + process.exit(1); } - debug('Copy static assets to dist folder.') - fs.copySync(paths.client('static'), paths.dist()) + debug('Copy static assets to dist folder.'); + fs.copySync(paths.client('static'), paths.dist()); } catch (e) { - debug('Compiler encountered an error.', e) - process.exit(1) + debug('Compiler encountered an error.', e); + process.exit(1); } -})() +})(); diff --git a/bin/server.js b/bin/server.js index 07f2dae..5460853 100644 --- a/bin/server.js +++ b/bin/server.js @@ -1,11 +1,11 @@ -import config from '../config' -import server from '../server/main' -import _debug from 'debug' +import config from '../config'; +import server from '../server/main'; +import _debug from 'debug'; -const debug = _debug('app:bin:server') -const port = config.server_port -const host = config.server_host +const debug = _debug('app:bin:server'); +const port = config.server_port; +const host = config.server_host; -server.listen(port) -debug(`Server is now running at http://${host}:${port}.`) -debug(`Server accessible via localhost:${port} if you are using the project defaults.`) +server.listen(port); +debug(`Server is now running at http://${host}:${port}.`); +debug(`Server accessible via localhost:${port} if you are using the project defaults.`); diff --git a/blueprints/blueprint/index.js b/blueprints/blueprint/index.js index d0660e1..86d5025 100644 --- a/blueprints/blueprint/index.js +++ b/blueprints/blueprint/index.js @@ -1,13 +1,13 @@ module.exports = { - description () { - return 'generates a blueprint and definition' + description() { + return 'generates a blueprint and definition'; }, - beforeInstall () { - console.log('Before installation hook!') + beforeInstall() { + console.log('Before installation hook!'); }, - afterInstall () { - console.log('After installation hook!') - } -} + afterInstall() { + console.log('After installation hook!'); + }, +}; diff --git a/blueprints/duck/index.js b/blueprints/duck/index.js index 033ff45..560b28a 100644 --- a/blueprints/duck/index.js +++ b/blueprints/duck/index.js @@ -1,5 +1,5 @@ module.exports = { - description () { - return 'generates a redux duck' - } -} + description() { + return 'generates a redux duck'; + }, +}; diff --git a/blueprints/dumb/index.js b/blueprints/dumb/index.js index b0c217a..63792ac 100644 --- a/blueprints/dumb/index.js +++ b/blueprints/dumb/index.js @@ -1,5 +1,5 @@ module.exports = { - description () { - return 'generates a dumb (pure) component' - } -} + description() { + return 'generates a dumb (pure) component'; + }, +}; diff --git a/blueprints/form/index.js b/blueprints/form/index.js index a6a8d19..f3ba55a 100644 --- a/blueprints/form/index.js +++ b/blueprints/form/index.js @@ -1,5 +1,5 @@ module.exports = { - description () { - return 'generates a connected redux-form form component' - } -} + description() { + return 'generates a connected redux-form form component'; + }, +}; diff --git a/blueprints/layout/index.js b/blueprints/layout/index.js index 31630e8..f9a7e0d 100644 --- a/blueprints/layout/index.js +++ b/blueprints/layout/index.js @@ -1,5 +1,5 @@ module.exports = { - description () { - return 'generates a functional layout component' - } -} + description() { + return 'generates a functional layout component'; + }, +}; diff --git a/blueprints/smart/index.js b/blueprints/smart/index.js index 1a1402f..78aed7e 100644 --- a/blueprints/smart/index.js +++ b/blueprints/smart/index.js @@ -1,13 +1,13 @@ module.exports = { - description () { - return 'generates a smart (container) component' + description() { + return 'generates a smart (container) component'; }, - fileMapTokens () { + fileMapTokens() { return { __smart__: (options) => { - return options.settings.getSetting('smartPath') - } - } - } -} + return options.settings.getSetting('smartPath'); + }, + }; + }, +}; diff --git a/blueprints/view/index.js b/blueprints/view/index.js index 5316a09..00b4110 100644 --- a/blueprints/view/index.js +++ b/blueprints/view/index.js @@ -1,5 +1,5 @@ module.exports = { - description () { - return 'generates a view component' - } -} + description() { + return 'generates a view component'; + }, +}; diff --git a/build/karma.conf.js b/build/karma.conf.js index 29c0f03..978fb41 100644 --- a/build/karma.conf.js +++ b/build/karma.conf.js @@ -1,10 +1,10 @@ -import { argv } from 'yargs' -import config from '../config' -import webpackConfig from './webpack.config' -import _debug from 'debug' +import { argv } from 'yargs'; +import config from '../config'; +import webpackConfig from './webpack.config'; +import _debug from 'debug'; -const debug = _debug('app:karma') -debug('Create configuration.') +const debug = _debug('app:karma'); +debug('Create configuration.'); const karmaConfig = { basePath: '../', // project root in relation to bin/karma.js @@ -14,14 +14,14 @@ const karmaConfig = { pattern: `./${config.dir_test}/test-bundler.js`, watched: false, served: true, - included: true - } + included: true, + }, ], singleRun: !argv.watch, frameworks: ['mocha'], reporters: ['mocha'], preprocessors: { - [`${config.dir_test}/test-bundler.js`]: ['webpack'] + [`${config.dir_test}/test-bundler.js`]: ['webpack'], }, browsers: ['PhantomJS'], webpack: { @@ -30,20 +30,20 @@ const karmaConfig = { ...webpackConfig.resolve, alias: { ...webpackConfig.resolve.alias, - sinon: 'sinon/pkg/sinon.js' - } + sinon: 'sinon/pkg/sinon.js', + }, }, plugins: webpackConfig.plugins, module: { noParse: [ - /\/sinon\.js/ + /\/sinon\.js/, ], loaders: webpackConfig.module.loaders.concat([ { test: /sinon(\\|\/)pkg(\\|\/)sinon\.js/, - loader: 'imports?define=>false,require=>false' - } - ]) + loader: 'imports?define=>false,require=>false', + }, + ]), }, // Enzyme fix, see: // https://github.com/airbnb/enzyme/issues/47 @@ -51,27 +51,27 @@ const karmaConfig = { ...webpackConfig.externals, 'react/addons': true, 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': 'window' + 'react/lib/ReactContext': 'window', }, - sassLoader: webpackConfig.sassLoader + sassLoader: webpackConfig.sassLoader, }, webpackMiddleware: { - noInfo: true + noInfo: true, }, coverageReporter: { - reporters: config.coverage_reporters - } -} + reporters: config.coverage_reporters, + }, +}; if (config.globals.__COVERAGE__) { - karmaConfig.reporters.push('coverage') + karmaConfig.reporters.push('coverage'); karmaConfig.webpack.module.preLoaders = [{ test: /\.(js|jsx)$/, include: new RegExp(config.dir_client), loader: 'isparta', - exclude: /node_modules/ - }] + exclude: /node_modules/, + }]; } // cannot use `export default` because of Karma. -module.exports = (cfg) => cfg.set(karmaConfig) +module.exports = (cfg) => cfg.set(karmaConfig); diff --git a/build/webpack-compiler.js b/build/webpack-compiler.js index 5e150f0..36d8c8a 100644 --- a/build/webpack-compiler.js +++ b/build/webpack-compiler.js @@ -1,35 +1,35 @@ -import webpack from 'webpack' -import _debug from 'debug' -import config from '../config' +import webpack from 'webpack'; +import _debug from 'debug'; +import config from '../config'; -const debug = _debug('app:build:webpack-compiler') -const DEFAULT_STATS_FORMAT = config.compiler_stats +const debug = _debug('app:build:webpack-compiler'); +const DEFAULT_STATS_FORMAT = config.compiler_stats; -export default function webpackCompiler (webpackConfig, statsFormat = DEFAULT_STATS_FORMAT) { +export default function webpackCompiler(webpackConfig, statsFormat = DEFAULT_STATS_FORMAT) { return new Promise((resolve, reject) => { - const compiler = webpack(webpackConfig) + const compiler = webpack(webpackConfig); compiler.run((err, stats) => { - const jsonStats = stats.toJson() + const jsonStats = stats.toJson(); - debug('Webpack compile completed.') - debug(stats.toString(statsFormat)) + debug('Webpack compile completed.'); + debug(stats.toString(statsFormat)); if (err) { - debug('Webpack compiler encountered a fatal error.', err) - return reject(err) + debug('Webpack compiler encountered a fatal error.', err); + return reject(err); } else if (jsonStats.errors.length > 0) { - debug('Webpack compiler encountered errors.') - debug(jsonStats.errors.join('\n')) - return reject(new Error('Webpack compiler encountered errors')) + debug('Webpack compiler encountered errors.'); + debug(jsonStats.errors.join('\n')); + return reject(new Error('Webpack compiler encountered errors')); } else if (jsonStats.warnings.length > 0) { - debug('Webpack compiler encountered warnings.') - debug(jsonStats.warnings.join('\n')) + debug('Webpack compiler encountered warnings.'); + debug(jsonStats.warnings.join('\n')); } else { - debug('No errors or warnings encountered.') + debug('No errors or warnings encountered.'); } - resolve(jsonStats) - }) - }) + resolve(jsonStats); + }); + }); } diff --git a/build/webpack.config.js b/build/webpack.config.js index 7962f66..111671e 100644 --- a/build/webpack.config.js +++ b/build/webpack.config.js @@ -1,36 +1,36 @@ -import webpack from 'webpack' -import cssnano from 'cssnano' -import HtmlWebpackPlugin from 'html-webpack-plugin' -import ExtractTextPlugin from 'extract-text-webpack-plugin' -import config from '../config' -import _debug from 'debug' +import webpack from 'webpack'; +import cssnano from 'cssnano'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import ExtractTextPlugin from 'extract-text-webpack-plugin'; +import config from '../config'; +import _debug from 'debug'; -const debug = _debug('app:webpack:config') -const paths = config.utils_paths -const {__DEV__, __PROD__, __TEST__} = config.globals +const debug = _debug('app:webpack:config'); +const paths = config.utils_paths; +const { __DEV__, __PROD__, __TEST__ } = config.globals; -debug('Create configuration.') +debug('Create configuration.'); const webpackConfig = { name: 'client', target: 'web', devtool: config.compiler_devtool, resolve: { root: paths.client(), - extensions: ['', '.js', '.jsx', '.json'] + extensions: ['', '.js', '.jsx', '.json'], }, - module: {} -} + module: {}, +}; // ------------------------------------ // Entry Points // ------------------------------------ -const APP_ENTRY_PATH = paths.client('main.js') +const APP_ENTRY_PATH = paths.client('main.js'); webpackConfig.entry = { app: __DEV__ ? [APP_ENTRY_PATH, `webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`] : [APP_ENTRY_PATH], - vendor: config.compiler_vendor -} + vendor: config.compiler_vendor, +}; // ------------------------------------ // Bundle Output @@ -38,8 +38,8 @@ webpackConfig.entry = { webpackConfig.output = { filename: `[name].[${config.compiler_hash_type}].js`, path: paths.dist(), - publicPath: config.compiler_public_path -} + publicPath: config.compiler_public_path, +}; // ------------------------------------ // Plugins @@ -53,19 +53,19 @@ webpackConfig.plugins = [ filename: 'index.html', inject: 'body', minify: { - collapseWhitespace: true - } - }) -] + collapseWhitespace: true, + }, + }), +]; if (__DEV__) { - debug('Enable plugins for live development (HMR, NoErrors).') + debug('Enable plugins for live development (HMR, NoErrors).'); webpackConfig.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() - ) + ); } else if (__PROD__) { - debug('Enable plugins for production (OccurenceOrder, Dedupe & UglifyJS).') + debug('Enable plugins for production (OccurenceOrder, Dedupe & UglifyJS).'); webpackConfig.plugins.push( new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.DedupePlugin(), @@ -73,19 +73,19 @@ if (__DEV__) { compress: { unused: true, dead_code: true, - warnings: false - } + warnings: false, + }, }) - ) + ); } // Don't split bundles during testing, since we only want import one bundle if (!__TEST__) { webpackConfig.plugins.push( new webpack.optimize.CommonsChunkPlugin({ - names: ['vendor'] + names: ['vendor'], }) - ) + ); } // ------------------------------------ @@ -128,38 +128,38 @@ webpackConfig.module.loaders = [{ presets: ['es2015', 'react', 'stage-0'], env: { production: { - presets: ['react-optimize'] - } - } - } + presets: ['react-optimize'], + }, + }, + }, }, { test: /\.json$/, - loader: 'json' -}] + loader: 'json', +}]; // ------------------------------------ // Style Loaders // ------------------------------------ // We use cssnano with the postcss loader, so we tell // css-loader not to duplicate minimization. -const BASE_CSS_LOADER = 'css?sourceMap&-minimize' +const BASE_CSS_LOADER = 'css?sourceMap&-minimize'; // Add any packge names here whose styles need to be treated as CSS modules. // These paths will be combined into a single regex. const PATHS_TO_TREAT_AS_CSS_MODULES = [ // 'react-toolbox', (example) -] +]; // If config has CSS modules enabled, treat this project's styles as CSS modules. if (config.compiler_css_modules) { PATHS_TO_TREAT_AS_CSS_MODULES.push( paths.client().replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&') // eslint-disable-line - ) + ); } -const isUsingCSSModules = !!PATHS_TO_TREAT_AS_CSS_MODULES.length -const cssModulesRegex = new RegExp(`(${PATHS_TO_TREAT_AS_CSS_MODULES.join('|')})`) +const isUsingCSSModules = !!PATHS_TO_TREAT_AS_CSS_MODULES.length; +const cssModulesRegex = new RegExp(`(${PATHS_TO_TREAT_AS_CSS_MODULES.join('|')})`); // Loaders for styles that need to be treated as CSS modules. if (isUsingCSSModules) { @@ -167,8 +167,8 @@ if (isUsingCSSModules) { BASE_CSS_LOADER, 'modules', 'importLoaders=1', - 'localIdentName=[name]__[local]___[hash:base64:5]' - ].join('&') + 'localIdentName=[name]__[local]___[hash:base64:5]', + ].join('&'); webpackConfig.module.loaders.push({ test: /\.scss$/, @@ -177,9 +177,9 @@ if (isUsingCSSModules) { 'style', cssModulesLoader, 'postcss', - 'sass?sourceMap' - ] - }) + 'sass?sourceMap', + ], + }); webpackConfig.module.loaders.push({ test: /\.css$/, @@ -187,13 +187,13 @@ if (isUsingCSSModules) { loaders: [ 'style', cssModulesLoader, - 'postcss' - ] - }) + 'postcss', + ], + }); } // Loaders for files that should not be treated as CSS modules. -const excludeCSSModules = isUsingCSSModules ? cssModulesRegex : false +const excludeCSSModules = isUsingCSSModules ? cssModulesRegex : false; webpackConfig.module.loaders.push({ test: /\.scss$/, exclude: excludeCSSModules, @@ -201,43 +201,43 @@ webpackConfig.module.loaders.push({ 'style', BASE_CSS_LOADER, 'postcss', - 'sass?sourceMap' - ] -}) + 'sass?sourceMap', + ], +}); webpackConfig.module.loaders.push({ test: /\.css$/, exclude: excludeCSSModules, loaders: [ 'style', BASE_CSS_LOADER, - 'postcss' - ] -}) + 'postcss', + ], +}); // ------------------------------------ // Style Configuration // ------------------------------------ webpackConfig.sassLoader = { - includePaths: paths.client('styles') -} + includePaths: paths.client('styles'), +}; webpackConfig.postcss = [ cssnano({ autoprefixer: { add: true, remove: true, - browsers: ['last 2 versions'] + browsers: ['last 2 versions'], }, discardComments: { - removeAll: true + removeAll: true, }, discardUnused: false, mergeIdents: false, reduceIdents: false, safe: true, - sourcemap: true - }) -] + sourcemap: true, + }), +]; // File loaders /* eslint-disable */ @@ -259,20 +259,20 @@ webpackConfig.module.loaders.push( // need to use the extractTextPlugin to fix this issue: // http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809 if (!__DEV__) { - debug('Apply ExtractTextPlugin to CSS loaders.') + debug('Apply ExtractTextPlugin to CSS loaders.'); webpackConfig.module.loaders.filter((loader) => loader.loaders && loader.loaders.find((name) => /css/.test(name.split('?')[0])) ).forEach((loader) => { - const [first, ...rest] = loader.loaders - loader.loader = ExtractTextPlugin.extract(first, rest.join('!')) - Reflect.deleteProperty(loader, 'loaders') - }) + const [first, ...rest] = loader.loaders; + loader.loader = ExtractTextPlugin.extract(first, rest.join('!')); + Reflect.deleteProperty(loader, 'loaders'); + }); webpackConfig.plugins.push( new ExtractTextPlugin('[name].[contenthash].css', { - allChunks: true + allChunks: true, }) - ) + ); } -export default webpackConfig +export default webpackConfig; diff --git a/config/environments.js b/config/environments.js index 9bef69f..2379af4 100644 --- a/config/environments.js +++ b/config/environments.js @@ -14,9 +14,9 @@ export default { enabled: false, options: { host: 'http://localhost:8000', - match: /^\/api\/.*/ - } - } + match: /^\/api\/.*/, + }, + }, }), // ====================================================== @@ -30,7 +30,7 @@ export default { compiler_stats: { chunks: true, chunkModules: true, - colors: true - } - }) -} + colors: true, + }, + }), +}; diff --git a/config/index.js b/config/index.js index 70fe44d..536a80d 100644 --- a/config/index.js +++ b/config/index.js @@ -1,12 +1,12 @@ /* eslint key-spacing:0 spaced-comment:0 */ -import path from 'path' -import _debug from 'debug' -import { argv } from 'yargs' -import ip from 'ip' +import path from 'path'; +import _debug from 'debug'; +import { argv } from 'yargs'; +import ip from 'ip'; -const localip = ip.address() -const debug = _debug('app:config') -debug('Creating default configuration.') +const localip = ip.address(); +const debug = _debug('app:config'); +debug('Creating default configuration.'); // ======================================================== // Default Configuration @@ -41,7 +41,7 @@ const config = { compiler_stats : { chunks : false, chunkModules : false, - colors : true + colors : true, }, compiler_vendor : [ 'history', @@ -49,7 +49,7 @@ const config = { 'react-redux', 'react-router', 'react-router-redux', - 'redux' + 'redux', ], // ---------------------------------- @@ -57,9 +57,9 @@ const config = { // ---------------------------------- coverage_reporters : [ { type : 'text-summary' }, - { type : 'lcov', dir : 'coverage' } - ] -} + { type : 'lcov', dir : 'coverage' }, + ], +}; /************************************************ ------------------------------------------------- @@ -76,7 +76,7 @@ Edit at Your Own Risk // N.B.: globals added here must _also_ be added to .eslintrc config.globals = { 'process.env' : { - 'NODE_ENV' : JSON.stringify(config.env) + 'NODE_ENV' : JSON.stringify(config.env), }, 'NODE_ENV' : config.env, '__DEV__' : config.env === 'development', @@ -84,49 +84,49 @@ config.globals = { '__TEST__' : config.env === 'test', '__DEBUG__' : config.env === 'development' && !argv.no_debug, '__COVERAGE__' : !argv.watch && config.env === 'test', - '__BASENAME__' : JSON.stringify(process.env.BASENAME || '') -} + '__BASENAME__' : JSON.stringify(process.env.BASENAME || ''), +}; // ------------------------------------ // Validate Vendor Dependencies // ------------------------------------ -const pkg = require('../package.json') +const pkg = require('../package.json'); config.compiler_vendor = config.compiler_vendor .filter((dep) => { - if (pkg.dependencies[dep]) return true + if (pkg.dependencies[dep]) return true; debug( `Package "${dep}" was not found as an npm dependency in package.json; ` + `it won't be included in the webpack vendor bundle. Consider removing it from vendor_dependencies in ~/config/index.js` - ) - }) + ); + }); // ------------------------------------ // Utilities // ------------------------------------ -const resolve = path.resolve +const resolve = path.resolve; const base = (...args) => - Reflect.apply(resolve, null, [config.path_base, ...args]) + Reflect.apply(resolve, null, [config.path_base, ...args]); config.utils_paths = { base : base, client : base.bind(null, config.dir_client), - dist : base.bind(null, config.dir_dist) -} + dist : base.bind(null, config.dir_dist), +}; // ======================================================== // Environment Configuration // ======================================================== -debug(`Looking for environment overrides for NODE_ENV "${config.env}".`) -const environments = require('./environments').default -const overrides = environments[config.env] +debug(`Looking for environment overrides for NODE_ENV "${config.env}".`); +const environments = require('./environments').default; +const overrides = environments[config.env]; if (overrides) { - debug('Found overrides, applying to default configuration.') - Object.assign(config, overrides(config)) + debug('Found overrides, applying to default configuration.'); + Object.assign(config, overrides(config)); } else { - debug('No environment overrides found, defaults will be used.') + debug('No environment overrides found, defaults will be used.'); } -export default config +export default config; diff --git a/package.json b/package.json index a6f8c13..c1c62f5 100644 --- a/package.json +++ b/package.json @@ -107,12 +107,16 @@ "node-sass": "^3.7.0", "normalize.css": "^4.1.1", "postcss-loader": "^0.9.0", + "query-string": "^4.1.0", "react": "^15.0.0", + "react-climb-social": "^2.0.0-alpha.4", "react-dom": "^15.0.0", "react-redux": "^4.0.0", "react-router": "^2.2.0", "react-router-redux": "^4.0.0", "redux": "^3.0.0", + "redux-actions": "^0.9.1", + "redux-debounced": "^0.2.0", "redux-thunk": "^2.0.0", "rimraf": "^2.5.1", "sass-loader": "^3.0.0", @@ -122,7 +126,7 @@ "yargs": "^4.0.0" }, "devDependencies": { - "babel-eslint": "^6.0.0-beta.6", + "babel-eslint": "^6.0.4", "chai": "^3.4.1", "chai-as-promised": "^5.1.0", "chai-enzyme": "^0.4.0", @@ -130,9 +134,12 @@ "codecov": "^1.0.1", "enzyme": "^2.0.0", "eslint": "^2.4.0", + "eslint-config-airbnb": "^9.0.1", "eslint-config-standard": "^5.1.0", "eslint-config-standard-react": "^2.2.0", "eslint-plugin-babel": "^3.0.0", + "eslint-plugin-import": "^1.8.0", + "eslint-plugin-jsx-a11y": "^1.2.0", "eslint-plugin-promise": "^1.0.8", "eslint-plugin-react": "^5.0.0", "eslint-plugin-standard": "^1.3.1", diff --git a/server/lib/apply-express-middleware.js b/server/lib/apply-express-middleware.js index 4c8ba2d..0f15764 100644 --- a/server/lib/apply-express-middleware.js +++ b/server/lib/apply-express-middleware.js @@ -1,14 +1,14 @@ // Based on: https://github.com/dayAlone/koa-webpack-hot-middleware/blob/master/index.js -export default function applyExpressMiddleware (fn, req, res) { - const originalEnd = res.end +export default function applyExpressMiddleware(fn, req, res) { + const originalEnd = res.end; return new Promise((resolve) => { res.end = function () { - originalEnd.apply(this, arguments) - resolve(false) - } + originalEnd.apply(this, arguments); + resolve(false); + }; fn(req, res, function () { - resolve(true) - }) - }) + resolve(true); + }); + }); } diff --git a/server/main.js b/server/main.js index 15e39bc..865c5f6 100644 --- a/server/main.js +++ b/server/main.js @@ -1,48 +1,48 @@ -import Koa from 'koa' -import convert from 'koa-convert' -import webpack from 'webpack' -import webpackConfig from '../build/webpack.config' -import historyApiFallback from 'koa-connect-history-api-fallback' -import serve from 'koa-static' -import proxy from 'koa-proxy' -import _debug from 'debug' -import config from '../config' -import webpackDevMiddleware from './middleware/webpack-dev' -import webpackHMRMiddleware from './middleware/webpack-hmr' - -const debug = _debug('app:server') -const paths = config.utils_paths -const app = new Koa() +import Koa from 'koa'; +import convert from 'koa-convert'; +import webpack from 'webpack'; +import webpackConfig from '../build/webpack.config'; +import historyApiFallback from 'koa-connect-history-api-fallback'; +import serve from 'koa-static'; +import proxy from 'koa-proxy'; +import _debug from 'debug'; +import config from '../config'; +import webpackDevMiddleware from './middleware/webpack-dev'; +import webpackHMRMiddleware from './middleware/webpack-hmr'; + +const debug = _debug('app:server'); +const paths = config.utils_paths; +const app = new Koa(); // Enable koa-proxy if it has been enabled in the config. if (config.proxy && config.proxy.enabled) { - app.use(convert(proxy(config.proxy.options))) + app.use(convert(proxy(config.proxy.options))); } // This rewrites all routes requests to the root /index.html file // (ignoring file requests). If you want to implement isomorphic // rendering, you'll want to remove this middleware. app.use(convert(historyApiFallback({ - verbose: false -}))) + verbose: false, +}))); // ------------------------------------ // Apply Webpack HMR Middleware // ------------------------------------ if (config.env === 'development') { - const compiler = webpack(webpackConfig) + const compiler = webpack(webpackConfig); // Enable webpack-dev and webpack-hot middleware - const { publicPath } = webpackConfig.output + const { publicPath } = webpackConfig.output; - app.use(webpackDevMiddleware(compiler, publicPath)) - app.use(webpackHMRMiddleware(compiler)) + app.use(webpackDevMiddleware(compiler, publicPath)); + app.use(webpackHMRMiddleware(compiler)); // Serve static assets from ~/src/static since Webpack is unaware of // these files. This middleware doesn't need to be enabled outside // of development since this directory will be copied into ~/dist // when the application is compiled. - app.use(convert(serve(paths.client('static')))) + app.use(convert(serve(paths.client('static')))); } else { debug( 'Server is being run outside of live development mode, meaning it will ' + @@ -50,12 +50,12 @@ if (config.env === 'development') { 'do not need an application server for this and can instead use a web ' + 'server such as nginx to serve your static files. See the "deployment" ' + 'section in the README for more information on deployment strategies.' - ) + ); // Serving ~/dist by default. Ideally these files should be served by // the web server and not the app server, but this helps to demo the // server in production. - app.use(convert(serve(paths.dist()))) + app.use(convert(serve(paths.dist()))); } -export default app +export default app; diff --git a/server/middleware/webpack-dev.js b/server/middleware/webpack-dev.js index 8187ba6..21be1c7 100644 --- a/server/middleware/webpack-dev.js +++ b/server/middleware/webpack-dev.js @@ -1,13 +1,13 @@ -import WebpackDevMiddleware from 'webpack-dev-middleware' -import applyExpressMiddleware from '../lib/apply-express-middleware' -import _debug from 'debug' -import config from '../../config' +import WebpackDevMiddleware from 'webpack-dev-middleware'; +import applyExpressMiddleware from '../lib/apply-express-middleware'; +import _debug from 'debug'; +import config from '../../config'; -const paths = config.utils_paths -const debug = _debug('app:server:webpack-dev') +const paths = config.utils_paths; +const debug = _debug('app:server:webpack-dev'); export default function (compiler, publicPath) { - debug('Enable webpack dev middleware.') + debug('Enable webpack dev middleware.'); const middleware = WebpackDevMiddleware(compiler, { publicPath, @@ -16,19 +16,19 @@ export default function (compiler, publicPath) { quiet: config.compiler_quiet, noInfo: config.compiler_quiet, lazy: false, - stats: config.compiler_stats - }) + stats: config.compiler_stats, + }); - return async function koaWebpackDevMiddleware (ctx, next) { + return async function koaWebpackDevMiddleware(ctx, next) { let hasNext = await applyExpressMiddleware(middleware, ctx.req, { end: (content) => (ctx.body = content), setHeader: function () { - ctx.set.apply(ctx, arguments) - } - }) + ctx.set.apply(ctx, arguments); + }, + }); if (hasNext) { - await next() + await next(); } - } + }; } diff --git a/server/middleware/webpack-hmr.js b/server/middleware/webpack-hmr.js index ddaff4d..738de93 100644 --- a/server/middleware/webpack-hmr.js +++ b/server/middleware/webpack-hmr.js @@ -1,18 +1,18 @@ -import WebpackHotMiddleware from 'webpack-hot-middleware' -import applyExpressMiddleware from '../lib/apply-express-middleware' -import _debug from 'debug' +import WebpackHotMiddleware from 'webpack-hot-middleware'; +import applyExpressMiddleware from '../lib/apply-express-middleware'; +import _debug from 'debug'; -const debug = _debug('app:server:webpack-hmr') +const debug = _debug('app:server:webpack-hmr'); export default function (compiler, opts) { - debug('Enable Webpack Hot Module Replacement (HMR).') + debug('Enable Webpack Hot Module Replacement (HMR).'); - const middleware = WebpackHotMiddleware(compiler, opts) - return async function koaWebpackHMR (ctx, next) { - let hasNext = await applyExpressMiddleware(middleware, ctx.req, ctx.res) + const middleware = WebpackHotMiddleware(compiler, opts); + return async function koaWebpackHMR(ctx, next) { + let hasNext = await applyExpressMiddleware(middleware, ctx.req, ctx.res); if (hasNext && next) { - await next() + await next(); } - } + }; } diff --git a/src/components/Counter/index.js b/src/components/Counter/index.js deleted file mode 100644 index ef982b9..0000000 --- a/src/components/Counter/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Counter from './Counter' - -export default Counter diff --git a/src/components/DisplayPicker/DisplayPicker.js b/src/components/DisplayPicker/DisplayPicker.js new file mode 100644 index 0000000..ddb4cc6 --- /dev/null +++ b/src/components/DisplayPicker/DisplayPicker.js @@ -0,0 +1,38 @@ +import React, { PropTypes } from 'react'; +import Navigation from './DisplayPickerNavigation'; +import Stage from './DisplayPickerStage'; +import EmbedCode from './DisplayPickerEmbedCode'; + +import FastInput from 'components/FastInput'; + +const DisplayPicker = ({ collectionId, setCollection, selected }) => ( +
+ + + + + + + +
+); + + +DisplayPicker.propTypes = { + collectionId: PropTypes.string, + options: PropTypes.arrayOf(PropTypes.object).isRequired, + selected: PropTypes.object.isRequired, + setDisplay: PropTypes.func.isRequired, + setCollection: PropTypes.func.isRequired, +}; + +export default DisplayPicker; diff --git a/src/components/DisplayPicker/DisplayPickerEmbedCode.js b/src/components/DisplayPicker/DisplayPickerEmbedCode.js new file mode 100644 index 0000000..3850057 --- /dev/null +++ b/src/components/DisplayPicker/DisplayPickerEmbedCode.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const DisplayPickerEmbedCode = () => ( +
+

Embed Code

+
+); + +export default DisplayPickerEmbedCode; diff --git a/src/components/DisplayPicker/DisplayPickerNavigation.js b/src/components/DisplayPicker/DisplayPickerNavigation.js new file mode 100644 index 0000000..1d5bf44 --- /dev/null +++ b/src/components/DisplayPicker/DisplayPickerNavigation.js @@ -0,0 +1,32 @@ +import React, { PropTypes } from 'react'; +import PushButton from 'components/PushButton'; + +export class DisplayPickerNavigation extends React.Component { + static propTypes = { + options: PropTypes.arrayOf(PropTypes.object).isRequired, + selected: PropTypes.object.isRequired, + setDisplay: PropTypes.func.isRequired, + }; + + static defaultProps = { + options: [], + }; + + render() { + const { options, selected, setDisplay } = this.props; + + return ( + + ); + } +} + +export default DisplayPickerNavigation; diff --git a/src/components/DisplayPicker/DisplayPickerStage.js b/src/components/DisplayPicker/DisplayPickerStage.js new file mode 100644 index 0000000..d8a3c0b --- /dev/null +++ b/src/components/DisplayPicker/DisplayPickerStage.js @@ -0,0 +1,20 @@ +import React, { PropTypes } from 'react'; +import { ClimbView } from 'react-climb-social'; + + +const DisplayPickerStage = ({ collectionId, layout }) => ( + +); + +DisplayPickerStage.propTypes = { + name: PropTypes.string, + layout: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + collectionId: PropTypes.string, +}; + +export default DisplayPickerStage; diff --git a/src/components/DisplayPicker/index.js b/src/components/DisplayPicker/index.js new file mode 100644 index 0000000..40cca88 --- /dev/null +++ b/src/components/DisplayPicker/index.js @@ -0,0 +1,2 @@ +import DisplayPicker from './DisplayPicker'; +export default DisplayPicker; diff --git a/src/components/FastInput/FastInput.js b/src/components/FastInput/FastInput.js new file mode 100644 index 0000000..9d993be --- /dev/null +++ b/src/components/FastInput/FastInput.js @@ -0,0 +1,39 @@ +import React, { PropTypes } from 'react'; + +export class FastInput extends React.Component { + + static propTypes = { + type: PropTypes.oneOf(['text']).isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + onChange: PropTypes.func.isRequired, + }; + + static defaultProps = { + type: 'text', + }; + + constructor(props) { + super(props); + this.state = { value: props.value }; + } + + handleChange(value) { + this.setState({ value }, () => this.props.onChange(value)); + } + + render() { + const { type } = this.props; + const { value } = this.state; + + return ( + this.handleChange(target.value)} + /> + ); + } +} + +export default FastInput; + diff --git a/src/components/FastInput/index.js b/src/components/FastInput/index.js new file mode 100644 index 0000000..76101ee --- /dev/null +++ b/src/components/FastInput/index.js @@ -0,0 +1,2 @@ +import FastInput from './FastInput'; +export default FastInput; diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 0acbe91..116d6d6 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,18 +1,10 @@ -import React from 'react' -import { IndexLink, Link } from 'react-router' -import classes from './Header.scss' - -export const Header = () => ( -
-

React Redux Starter Kit

- - Home - - {' · '} - - Counter - -
-) - -export default Header +import React from 'react'; +// import classes from './Header.scss'; + +export const Header = () => ( +
+

Climb Social

+
+); + +export default Header; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 7cd29d7..a9ce105 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,3 +1,3 @@ -import Header from './Header' +import Header from './Header'; -export default Header +export default Header; diff --git a/src/components/PushButton/PushButton.js b/src/components/PushButton/PushButton.js new file mode 100644 index 0000000..2507585 --- /dev/null +++ b/src/components/PushButton/PushButton.js @@ -0,0 +1,26 @@ +import React, { PropTypes } from 'react'; + +export class PushButton extends React.Component { + static propTypes = { + children: PropTypes.node.isRequired, + isActive: PropTypes.bool, + onClick: PropTypes.func, + }; + + static defaultProps = { + isActive: false, + }; + + render() { + const { isActive, ...others } = this.props; + + return ( + {' '} - -) +); Counter.propTypes = { counter: React.PropTypes.number.isRequired, doubleAsync: React.PropTypes.func.isRequired, - increment: React.PropTypes.func.isRequired -} + increment: React.PropTypes.func.isRequired, +}; -export default Counter +export default Counter; diff --git a/src/components/Counter/Counter.scss b/src/components/_Counter/Counter.scss similarity index 100% rename from src/components/Counter/Counter.scss rename to src/components/_Counter/Counter.scss diff --git a/src/components/_Counter/index.js b/src/components/_Counter/index.js new file mode 100644 index 0000000..97f7f53 --- /dev/null +++ b/src/components/_Counter/index.js @@ -0,0 +1,3 @@ +import Counter from './Counter'; + +export default Counter; diff --git a/src/components/_Header/Header.js b/src/components/_Header/Header.js new file mode 100644 index 0000000..9a7fd2e --- /dev/null +++ b/src/components/_Header/Header.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { IndexLink, Link } from 'react-router'; +import classes from './Header.scss'; + +export const Header = () => ( +
+

React Redux Starter Kit

+ + Home + + {' · '} + + Counter + +
+); + +export default Header; diff --git a/src/components/_Header/Header.scss b/src/components/_Header/Header.scss new file mode 100644 index 0000000..1dcd731 --- /dev/null +++ b/src/components/_Header/Header.scss @@ -0,0 +1,4 @@ +.activeRoute { + font-weight: bold; + text-decoration: underline; +} diff --git a/src/components/_Header/index.js b/src/components/_Header/index.js new file mode 100644 index 0000000..a9ce105 --- /dev/null +++ b/src/components/_Header/index.js @@ -0,0 +1,3 @@ +import Header from './Header'; + +export default Header; diff --git a/src/containers/AppContainer.js b/src/containers/AppContainer.js index f658a9e..f573716 100644 --- a/src/containers/AppContainer.js +++ b/src/containers/AppContainer.js @@ -1,26 +1,20 @@ -import React, { PropTypes } from 'react' -import { Router } from 'react-router' -import { Provider } from 'react-redux' +import React, { PropTypes } from 'react'; +import { Router } from 'react-router'; +import { Provider } from 'react-redux'; -class AppContainer extends React.Component { - static propTypes = { - history: PropTypes.object.isRequired, - routes: PropTypes.object.isRequired, - routerKey: PropTypes.number, - store: PropTypes.object.isRequired - } +const AppContainer = ({ history, routes, routerKey, store }) => ( + +
+ +
+
+); - render () { - const { history, routes, routerKey, store } = this.props +AppContainer.propTypes = { + history: PropTypes.object.isRequired, + routes: PropTypes.object.isRequired, + routerKey: PropTypes.number, + store: PropTypes.object.isRequired, +}; - return ( - -
- -
-
- ) - } -} - -export default AppContainer +export default AppContainer; diff --git a/src/index.html b/src/index.html index 08a85d6..69e0975 100644 --- a/src/index.html +++ b/src/index.html @@ -1,7 +1,7 @@ - React Redux Starter Kit + Climb Social Displayer diff --git a/src/layouts/CoreLayout/CoreLayout.js b/src/layouts/CoreLayout/CoreLayout.js index 58286e8..c26bf98 100644 --- a/src/layouts/CoreLayout/CoreLayout.js +++ b/src/layouts/CoreLayout/CoreLayout.js @@ -1,19 +1,19 @@ -import React from 'react' -import Header from '../../components/Header' -import classes from './CoreLayout.scss' -import '../../styles/core.scss' +import React from 'react'; +import Header from '../../components/Header'; +import classes from './CoreLayout.scss'; +import '../../styles/core.scss'; export const CoreLayout = ({ children }) => ( -
+
{children}
-) +); CoreLayout.propTypes = { - children: React.PropTypes.element.isRequired -} + children: React.PropTypes.element.isRequired, +}; -export default CoreLayout +export default CoreLayout; diff --git a/src/layouts/CoreLayout/index.js b/src/layouts/CoreLayout/index.js index 7fe88c1..bff4621 100644 --- a/src/layouts/CoreLayout/index.js +++ b/src/layouts/CoreLayout/index.js @@ -1,3 +1,3 @@ -import CoreLayout from './CoreLayout' +import CoreLayout from './CoreLayout'; -export default CoreLayout +export default CoreLayout; diff --git a/src/main.js b/src/main.js index 6d396ec..e501f38 100644 --- a/src/main.js +++ b/src/main.js @@ -1,17 +1,17 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import createBrowserHistory from 'history/lib/createBrowserHistory' -import { useRouterHistory } from 'react-router' -import { syncHistoryWithStore } from 'react-router-redux' -import createStore from './store/createStore' -import AppContainer from './containers/AppContainer' +import React from 'react'; +import ReactDOM from 'react-dom'; +import createBrowserHistory from 'history/lib/createBrowserHistory'; +import { useRouterHistory } from 'react-router'; +import { syncHistoryWithStore } from 'react-router-redux'; +import createStore from './store/createStore'; +import AppContainer from './containers/AppContainer'; // ======================================================== // Browser History Setup // ======================================================== const browserHistory = useRouterHistory(createBrowserHistory)({ - basename: __BASENAME__ -}) + basename: __BASENAME__, +}); // ======================================================== // Store and History Instantiation @@ -20,28 +20,28 @@ const browserHistory = useRouterHistory(createBrowserHistory)({ // react-router-redux reducer under the routerKey "router" in src/routes/index.js, // so we need to provide a custom `selectLocationState` to inform // react-router-redux of its location. -const initialState = window.___INITIAL_STATE__ -const store = createStore(initialState, browserHistory) +const initialState = window.___INITIAL_STATE__; +const store = createStore(initialState, browserHistory); const history = syncHistoryWithStore(browserHistory, store, { - selectLocationState: (state) => state.router -}) + selectLocationState: (state) => state.router, +}); // ======================================================== // Developer Tools Setup // ======================================================== if (__DEBUG__) { if (window.devToolsExtension) { - window.devToolsExtension.open() + window.devToolsExtension.open(); } } // ======================================================== // Render Setup // ======================================================== -const MOUNT_NODE = document.getElementById('root') +const MOUNT_NODE = document.getElementById('root'); let render = (routerKey = null) => { - const routes = require('./routes/index').default(store) + const routes = require('./routes/index').default(store); ReactDOM.render( { routerKey={routerKey} />, MOUNT_NODE - ) -} + ); +}; // Enable HMR and catch runtime errors in RedBox // This code is excluded from production bundle if (__DEV__ && module.hot) { - const renderApp = render + const renderApp = render; const renderError = (error) => { - const RedBox = require('redbox-react') + const RedBox = require('redbox-react'); - ReactDOM.render(, MOUNT_NODE) - } + ReactDOM.render(, MOUNT_NODE); + }; render = () => { try { - renderApp(Math.random()) + renderApp(Math.random()); } catch (error) { - renderError(error) + renderError(error); } - } - module.hot.accept(['./routes/index'], () => render()) + }; + module.hot.accept(['./routes/index'], () => render()); } // ======================================================== // Go! // ======================================================== -render() +render(); diff --git a/src/routes/Home/components/HomeView.js b/src/routes/Home/components/HomeView.js index f1f6b96..221ca67 100644 --- a/src/routes/Home/components/HomeView.js +++ b/src/routes/Home/components/HomeView.js @@ -1,15 +1,19 @@ -import React from 'react' -import DuckImage from '../assets/Duck.jpg' -import classes from './HomeView.scss' +import React from 'react'; +// import classes from './HomeView.scss'; + +import DisplayPickerContainer from '../containers/DisplayPickerContainer'; export const HomeView = () => (
-

Welcome!

- This is a duck, because Redux! +

Publish this Collection

+

Pick a style. Get your embed code.

+ + + +
+

HowToGuideBlock

+
-) +); -export default HomeView +export default HomeView; diff --git a/src/routes/Home/components/HomeView.scss b/src/routes/Home/components/HomeView.scss index 10de960..e69de29 100644 --- a/src/routes/Home/components/HomeView.scss +++ b/src/routes/Home/components/HomeView.scss @@ -1,5 +0,0 @@ -.duck { - display: block; - width: 120px; - margin: 1.5rem auto; -} diff --git a/src/routes/Home/containers/DisplayPickerContainer.js b/src/routes/Home/containers/DisplayPickerContainer.js new file mode 100644 index 0000000..17d2e22 --- /dev/null +++ b/src/routes/Home/containers/DisplayPickerContainer.js @@ -0,0 +1,20 @@ +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { actions as displayActions } from '../modules/display'; + +import DisplayPicker from 'components/DisplayPicker'; + + +const mapStateToProps = (state) => ({ + collectionId: state.display.collectionId, + options: state.display.options, + selected: state.display.selected, +}); + +const mapDispatchToProps = (dispatch) => + bindActionCreators(displayActions, dispatch); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(DisplayPicker); diff --git a/src/routes/Home/index.js b/src/routes/Home/index.js index 984f23f..ccd9db6 100644 --- a/src/routes/Home/index.js +++ b/src/routes/Home/index.js @@ -1,6 +1,5 @@ -import HomeView from './components/HomeView' +import HomeView from './components/HomeView'; -// Sync route definition export default { - component: HomeView -} + component: HomeView, +}; diff --git a/src/routes/Home/modules/display.js b/src/routes/Home/modules/display.js new file mode 100644 index 0000000..5b447ca --- /dev/null +++ b/src/routes/Home/modules/display.js @@ -0,0 +1,51 @@ +import { createAction, handleActions } from 'redux-actions'; +import queryString from 'query-string'; + +const displayOptions = [ + { + key: 'list', + name: 'List', + }, + { + key: 'grid', + name: 'Grid', + layout: 'square', + }, +]; + +// Constants +export const COLLECTION_ID_SET = 'COLLECTION_ID_SET'; +export const DISPLAY_SET = 'DISPLAY_SET'; + +// Action Creators +export const setCollection = (payload) => ({ + type: COLLECTION_ID_SET, + payload, + meta: { + debounce: { + time: 300, + }, + }, +}); + +export const setDisplay = createAction(DISPLAY_SET); + +export const actions = { + setCollection, + setDisplay, +}; + + +// Reducer +const { collection } = queryString.parse(window.location.search); + +export const initialState = { + collectionId: collection, + options: displayOptions, + selected: displayOptions[0], +}; + +export default handleActions({ + [COLLECTION_ID_SET]: (state, { payload }) => ({ ...state, collectionId: payload }), + [DISPLAY_SET]: (state, { payload }) => ({ ...state, selected: payload }), +}, initialState); diff --git a/src/routes/Counter/containers/CounterContainer.js b/src/routes/_Counter/containers/CounterContainer.js similarity index 86% rename from src/routes/Counter/containers/CounterContainer.js rename to src/routes/_Counter/containers/CounterContainer.js index d0fe41a..c0352b0 100644 --- a/src/routes/Counter/containers/CounterContainer.js +++ b/src/routes/_Counter/containers/CounterContainer.js @@ -1,12 +1,12 @@ -import { connect } from 'react-redux' -import { increment, doubleAsync } from '../modules/counter' +import { connect } from 'react-redux'; +import { increment, doubleAsync } from '../modules/counter'; /* This is a container component. Notice it does not contain any JSX, nor does it import React. This component is **only** responsible for wiring in the actions and state necessary to render a presentational component - in this case, the counter: */ -import Counter from 'components/Counter' +import Counter from 'components/Counter'; /* Object of action creators (can also be function that returns object). Keys will be passed as props to presentational components. Here we are @@ -14,12 +14,12 @@ import Counter from 'components/Counter' const mapActionCreators = { increment: () => increment(1), - doubleAsync -} + doubleAsync, +}; const mapStateToProps = (state) => ({ - counter: state.counter -}) + counter: state.counter, +}); /* Note: mapStateToProps is where you should use `reselect` to create selectors, ie: @@ -35,4 +35,4 @@ const mapStateToProps = (state) => ({ Selectors are composable. They can be used as input to other selectors. https://github.com/reactjs/reselect */ -export default connect(mapStateToProps, mapActionCreators)(Counter) +export default connect(mapStateToProps, mapActionCreators)(Counter); diff --git a/src/routes/Counter/index.js b/src/routes/_Counter/index.js similarity index 68% rename from src/routes/Counter/index.js rename to src/routes/_Counter/index.js index bfb6c71..e8fcb2d 100644 --- a/src/routes/Counter/index.js +++ b/src/routes/_Counter/index.js @@ -1,24 +1,24 @@ -import { injectReducer } from '../../store/reducers' +import { injectReducer } from '../../store/reducers'; export default (store) => ({ path: 'counter', /* Async getComponent is only invoked when route matches */ - getComponent (nextState, cb) { + getComponent(nextState, cb) { /* Webpack - use 'require.ensure' to create a split point and embed an async module loader (jsonp) when bundling */ require.ensure([], (require) => { /* Webpack - use require callback to define dependencies for bundling */ - const Counter = require('./containers/CounterContainer').default - const reducer = require('./modules/counter').default + const Counter = require('./containers/CounterContainer').default; + const reducer = require('./modules/counter').default; /* Add the reducer to the store on key 'counter' */ - injectReducer(store, { key: 'counter', reducer }) + injectReducer(store, { key: 'counter', reducer }); /* Return getComponent */ - cb(null, Counter) + cb(null, Counter); /* Webpack named bundle */ - }, 'counter') - } -}) + }, 'counter'); + }, +}); diff --git a/src/routes/Counter/modules/counter.js b/src/routes/_Counter/modules/counter.js similarity index 64% rename from src/routes/Counter/modules/counter.js rename to src/routes/_Counter/modules/counter.js index 8283ced..7b23279 100644 --- a/src/routes/Counter/modules/counter.js +++ b/src/routes/_Counter/modules/counter.js @@ -1,16 +1,16 @@ // ------------------------------------ // Constants // ------------------------------------ -export const COUNTER_INCREMENT = 'COUNTER_INCREMENT' +export const COUNTER_INCREMENT = 'COUNTER_INCREMENT'; // ------------------------------------ // Actions // ------------------------------------ -export function increment (value = 1) { +export function increment(value = 1) { return { type: COUNTER_INCREMENT, - payload: value - } + payload: value, + }; } /* This is a thunk, meaning it is a function that immediately @@ -21,35 +21,33 @@ export function increment (value = 1) { you'd probably want to dispatch an action of COUNTER_DOUBLE and let the reducer take care of this logic. */ -export const doubleAsync = () => { - return (dispatch, getState) => { - return new Promise((resolve) => { +export const doubleAsync = () => + (dispatch, getState) => + new Promise((resolve) => { setTimeout(() => { - dispatch(increment(getState().counter)) - resolve() - }, 200) - }) - } -} + dispatch(increment(getState().counter)); + resolve(); + }, 200); + }); export const actions = { increment, - doubleAsync -} + doubleAsync, +}; // ------------------------------------ // Action Handlers // ------------------------------------ const ACTION_HANDLERS = { - [COUNTER_INCREMENT]: (state, action) => state + action.payload -} + [COUNTER_INCREMENT]: (state, action) => state + action.payload, +}; // ------------------------------------ // Reducer // ------------------------------------ -const initialState = 0 -export default function counterReducer (state = initialState, action) { - const handler = ACTION_HANDLERS[action.type] +const initialState = 0; +export default function counterReducer(state = initialState, action) { + const handler = ACTION_HANDLERS[action.type]; - return handler ? handler(state, action) : state + return handler ? handler(state, action) : state; } diff --git a/src/routes/Home/assets/Duck.jpg b/src/routes/_Home/assets/Duck.jpg similarity index 100% rename from src/routes/Home/assets/Duck.jpg rename to src/routes/_Home/assets/Duck.jpg diff --git a/src/routes/_Home/components/HomeView.js b/src/routes/_Home/components/HomeView.js new file mode 100644 index 0000000..77ed4df --- /dev/null +++ b/src/routes/_Home/components/HomeView.js @@ -0,0 +1,16 @@ +import React from 'react'; +import DuckImage from '../assets/Duck.jpg'; +import classes from './HomeView.scss'; + +export const HomeView = () => ( +
+

Welcome!

+ This is a duck, because Redux! +
+); + +export default HomeView; diff --git a/src/routes/_Home/components/HomeView.scss b/src/routes/_Home/components/HomeView.scss new file mode 100644 index 0000000..10de960 --- /dev/null +++ b/src/routes/_Home/components/HomeView.scss @@ -0,0 +1,5 @@ +.duck { + display: block; + width: 120px; + margin: 1.5rem auto; +} diff --git a/src/routes/_Home/index.js b/src/routes/_Home/index.js new file mode 100644 index 0000000..d937c77 --- /dev/null +++ b/src/routes/_Home/index.js @@ -0,0 +1,6 @@ +import HomeView from './components/HomeView'; + +// Sync route definition +export default { + component: HomeView, +}; diff --git a/src/routes/_index.js b/src/routes/_index.js new file mode 100644 index 0000000..9227ab9 --- /dev/null +++ b/src/routes/_index.js @@ -0,0 +1,36 @@ +// We only need to import the modules necessary for initial render +import CoreLayout from '../layouts/CoreLayout/CoreLayout'; +import Home from './Home'; +import CounterRoute from './Counter'; + +/* Note: Instead of using JSX, we recommend using react-router + PlainRoute objects to build route definitions. */ + +export const createRoutes = (store) => ({ + path: '/', + component: CoreLayout, + indexRoute: Home, + childRoutes: [ + new CounterRoute(store), + ], +}); + +/* Note: childRoutes can be chunked or otherwise loaded programmatically + using getChildRoutes with the following signature: + + getChildRoutes (location, cb) { + require.ensure([], (require) => { + cb(null, [ + // Remove imports! + require('./Counter').default(store) + ]) + }) + } + + However, this is not necessary for code-splitting! It simply provides + an API for async route definitions. Your code splitting should occur + inside the route `getComponent` function, since it is only invoked + when the route exists and matches. +*/ + +export default createRoutes; diff --git a/src/routes/index.js b/src/routes/index.js index 7b36956..3d7c753 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,36 +1,10 @@ -// We only need to import the modules necessary for initial render -import CoreLayout from '../layouts/CoreLayout/CoreLayout' -import Home from './Home' -import CounterRoute from './Counter' +import CoreLayout from '../layouts/CoreLayout/CoreLayout'; +import Home from './Home'; -/* Note: Instead of using JSX, we recommend using react-router - PlainRoute objects to build route definitions. */ - -export const createRoutes = (store) => ({ +export const createRoutes = (/* store */) => ({ path: '/', component: CoreLayout, indexRoute: Home, - childRoutes: [ - CounterRoute(store) - ] -}) - -/* Note: childRoutes can be chunked or otherwise loaded programmatically - using getChildRoutes with the following signature: - - getChildRoutes (location, cb) { - require.ensure([], (require) => { - cb(null, [ - // Remove imports! - require('./Counter').default(store) - ]) - }) - } - - However, this is not necessary for code-splitting! It simply provides - an API for async route definitions. Your code splitting should occur - inside the route `getComponent` function, since it is only invoked - when the route exists and matches. -*/ +}); -export default createRoutes +export default createRoutes; diff --git a/src/store/createStore.js b/src/store/createStore.js index 698fb31..c4f4319 100644 --- a/src/store/createStore.js +++ b/src/store/createStore.js @@ -1,22 +1,23 @@ -import { applyMiddleware, compose, createStore } from 'redux' -import { routerMiddleware } from 'react-router-redux' -import thunk from 'redux-thunk' -import makeRootReducer from './reducers' +import { applyMiddleware, compose, createStore } from 'redux'; +import { routerMiddleware } from 'react-router-redux'; +import createDebounce from 'redux-debounced'; +import thunk from 'redux-thunk'; +import makeRootReducer from './reducers'; export default (initialState = {}, history) => { // ====================================================== // Middleware Configuration // ====================================================== - const middleware = [thunk, routerMiddleware(history)] + const middleware = [thunk, routerMiddleware(history), createDebounce()]; // ====================================================== // Store Enhancers // ====================================================== - const enhancers = [] + const enhancers = []; if (__DEBUG__) { - const devToolsExtension = window.devToolsExtension + const devToolsExtension = window.devToolsExtension; if (typeof devToolsExtension === 'function') { - enhancers.push(devToolsExtension()) + enhancers.push(devToolsExtension()); } } @@ -30,15 +31,15 @@ export default (initialState = {}, history) => { applyMiddleware(...middleware), ...enhancers ) - ) - store.asyncReducers = {} + ); + store.asyncReducers = {}; if (module.hot) { module.hot.accept('./reducers', () => { - const reducers = require('./reducers').default - store.replaceReducer(reducers) - }) + const reducers = require('./reducers').default; + store.replaceReducer(reducers); + }); } - return store -} + return store; +}; diff --git a/src/store/reducers.js b/src/store/reducers.js index 3162282..0bd9a0b 100644 --- a/src/store/reducers.js +++ b/src/store/reducers.js @@ -1,17 +1,20 @@ -import { combineReducers } from 'redux' -import { routerReducer as router } from 'react-router-redux' +import { combineReducers } from 'redux'; +import { routerReducer as router } from 'react-router-redux'; -export const makeRootReducer = (asyncReducers) => { - return combineReducers({ +// Sync routes +import display from '../routes/Home/modules/display'; + +export const makeRootReducer = (asyncReducers) => + combineReducers({ // Add sync reducers here + display, router, - ...asyncReducers - }) -} + ...asyncReducers, + }); export const injectReducer = (store, { key, reducer }) => { - store.asyncReducers[key] = reducer - store.replaceReducer(makeRootReducer(store.asyncReducers)) -} + store.asyncReducers[key] = reducer; + store.replaceReducer(makeRootReducer(store.asyncReducers)); +}; -export default makeRootReducer +export default makeRootReducer; diff --git a/tests/components/DisplayPicker.spec.js b/tests/components/DisplayPicker.spec.js new file mode 100644 index 0000000..f3b00e3 --- /dev/null +++ b/tests/components/DisplayPicker.spec.js @@ -0,0 +1,8 @@ +import React from 'react' +import DisplayPicker from 'components/DisplayPicker/DisplayPicker' + +describe('(Component) DisplayPicker', () => { + it('should exist', () => { + + }) +}) diff --git a/tests/components/FastInput.spec.js b/tests/components/FastInput.spec.js new file mode 100644 index 0000000..fd97d30 --- /dev/null +++ b/tests/components/FastInput.spec.js @@ -0,0 +1,8 @@ +import React from 'react' +import FastInput from 'components/FastInput/FastInput' + +describe('(Component) FastInput', () => { + it('should exist', () => { + + }) +}) diff --git a/tests/components/PushButton.spec.js b/tests/components/PushButton.spec.js new file mode 100644 index 0000000..c187467 --- /dev/null +++ b/tests/components/PushButton.spec.js @@ -0,0 +1,8 @@ +import React from 'react' +import PushButton from 'components/PushButton/PushButton' + +describe('(Component) PushButton', () => { + it('should exist', () => { + + }) +}) diff --git a/tests/containers/DisplayPickerContainer.spec.js b/tests/containers/DisplayPickerContainer.spec.js new file mode 100644 index 0000000..4608642 --- /dev/null +++ b/tests/containers/DisplayPickerContainer.spec.js @@ -0,0 +1,5 @@ +describe('(Component) DisplayPickerContainer', () => { + it('exists', () => { + + }) +}) diff --git a/tests/routes/Home/modules/display.spec.js b/tests/routes/Home/modules/display.spec.js new file mode 100644 index 0000000..b6757eb --- /dev/null +++ b/tests/routes/Home/modules/display.spec.js @@ -0,0 +1,9 @@ +import reducer, { initialState } from 'redux/modules/display' + +describe('(Redux) display', () => { + describe('(Reducer)', () => { + it('sets up initial state', () => { + expect(reducer(undefined, {})).to.eql(initialState) + }) + }) +}) diff --git a/tests/test-bundler.js b/tests/test-bundler.js index 507082a..a15a524 100644 --- a/tests/test-bundler.js +++ b/tests/test-bundler.js @@ -1,20 +1,20 @@ // --------------------------------------- // Test Environment Setup // --------------------------------------- -import sinon from 'sinon' -import chai from 'chai' -import sinonChai from 'sinon-chai' -import chaiAsPromised from 'chai-as-promised' -import chaiEnzyme from 'chai-enzyme' +import sinon from 'sinon'; +import chai from 'chai'; +import sinonChai from 'sinon-chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiEnzyme from 'chai-enzyme'; -chai.use(sinonChai) -chai.use(chaiAsPromised) -chai.use(chaiEnzyme()) +chai.use(sinonChai); +chai.use(chaiAsPromised); +chai.use(chaiEnzyme()); -global.chai = chai -global.sinon = sinon -global.expect = chai.expect -global.should = chai.should() +global.chai = chai; +global.sinon = sinon; +global.expect = chai.expect; +global.should = chai.should(); // --------------------------------------- // Require Tests @@ -24,17 +24,17 @@ global.should = chai.should() // for some reason an array literal without a trailing `;` causes // some build environments to fail. const __karmaWebpackManifest__ = new Array() // eslint-disable-line -const inManifest = (path) => ~__karmaWebpackManifest__.indexOf(path) +const inManifest = (path) => ~__karmaWebpackManifest__.indexOf(path); // require all `tests/**/*.spec.js` -const testsContext = require.context('./', true, /\.spec\.js$/) +const testsContext = require.context('./', true, /\.spec\.js$/); // only run tests that have changed after the first pass. const testsToRun = testsContext.keys().filter(inManifest) -;(testsToRun.length ? testsToRun : testsContext.keys()).forEach(testsContext) +;(testsToRun.length ? testsToRun : testsContext.keys()).forEach(testsContext); // require all `src/**/*.js` except for `main.js` (for isparta coverage reporting) if (__COVERAGE__) { - const componentsContext = require.context('../src/', true, /^((?!main).)*\.js$/) - componentsContext.keys().forEach(componentsContext) + const componentsContext = require.context('../src/', true, /^((?!main).)*\.js$/); + componentsContext.keys().forEach(componentsContext); }