├── app
├── translations
│ └── en.json
├── containers
│ ├── App
│ │ ├── constants.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ └── selectors.test.js
│ │ ├── selectors.js
│ │ └── view.js
│ ├── HomePage
│ │ ├── constants.js
│ │ ├── actions.js
│ │ ├── sagas.js
│ │ ├── tests
│ │ │ ├── reducer.test.js
│ │ │ ├── index.test.js
│ │ │ ├── selectors.test.js
│ │ │ ├── sagas.test.js
│ │ │ └── actions.test.js
│ │ ├── messages.js
│ │ ├── reducer.js
│ │ ├── index.js
│ │ ├── selectors.js
│ │ └── view.js
│ ├── LanguageProvider
│ │ ├── constants.js
│ │ ├── actions.js
│ │ ├── view.js
│ │ ├── selectors.js
│ │ ├── reducer.js
│ │ └── index.js
│ └── NotFoundPage
│ │ ├── view.js
│ │ ├── messages.js
│ │ └── index.js
├── favicon.ico
├── components
│ ├── H1
│ │ ├── index.js
│ │ └── tests
│ │ │ └── index.test.js
│ ├── A
│ │ ├── index.js
│ │ └── tests
│ │ │ └── index.test.js
│ ├── Badge
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── Card
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── AppBar
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── Avatar
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── CardText
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── CardHeader
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── CardMedia
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── CardTitle
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── AutoComplete
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── CardActions
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ ├── BottomNavigation
│ │ ├── tests
│ │ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
│ └── BottomNavigationItem
│ │ ├── tests
│ │ └── index.test.js
│ │ ├── view.js
│ │ └── index.js
├── global-styles.js
├── tests
│ └── store.test.js
├── manifest.json
├── index.html
├── i18n.js
├── reducers.js
├── .nginx.conf
├── .htaccess
├── routes.js
├── store.js
├── utils
│ ├── asyncInjectors.js
│ └── tests
│ │ └── asyncInjectors.test.js
└── app.js
├── Procfile
├── internals
├── generators
│ ├── language
│ │ ├── translations-json.hbs
│ │ ├── app-locale.hbs
│ │ ├── add-locale-data.hbs
│ │ ├── polyfill-intl-locale.hbs
│ │ ├── intl-locale-data.hbs
│ │ ├── translation-messages.hbs
│ │ ├── format-translation-messages.hbs
│ │ └── index.js
│ ├── container
│ │ ├── constants.js.hbs
│ │ ├── actions.js.hbs
│ │ ├── sagas.js.hbs
│ │ ├── reducer.test.js.hbs
│ │ ├── test.js.hbs
│ │ ├── messages.js.hbs
│ │ ├── sagas.test.js.hbs
│ │ ├── selectors.test.js.hbs
│ │ ├── actions.test.js.hbs
│ │ ├── reducer.js.hbs
│ │ ├── selectors.js.hbs
│ │ ├── index.js.hbs
│ │ ├── view.js.hbs
│ │ └── index.js
│ ├── route
│ │ ├── route.hbs
│ │ ├── routeWithReducer.hbs
│ │ └── index.js
│ ├── component
│ │ ├── es6.js.hbs
│ │ ├── es6.pure.js.hbs
│ │ ├── test.js.hbs
│ │ ├── messages.js.hbs
│ │ ├── view.js.hbs
│ │ └── index.js
│ ├── utils
│ │ └── componentExists.js
│ └── index.js
├── scripts
│ ├── helpers
│ │ ├── checkmark.js
│ │ └── progress.js
│ ├── npmcheckversion.js
│ ├── pagespeed.js
│ ├── analyze.js
│ ├── dependencies.js
│ └── extract-intl.js
├── testing
│ ├── test-bundler.js
│ └── karma.conf.js
├── webpack
│ ├── webpack.dll.babel.js
│ ├── webpack.prod.babel.js
│ ├── webpack.test.babel.js
│ ├── webpack.base.babel.js
│ └── webpack.dev.babel.js
└── config.js
├── .lgtm
├── .editorconfig
├── docs
├── general
│ ├── webstorm-debug.png
│ ├── webstorm-eslint.png
│ ├── server-configs.md
│ ├── gotchas.md
│ ├── deployment.md
│ ├── files.md
│ ├── remove.md
│ ├── commands.md
│ ├── README.md
│ └── faq.md
├── css
│ ├── remove.md
│ ├── README.md
│ ├── sanitize.md
│ ├── sass.md
│ └── styled-componets.md
├── testing
│ ├── remote-testing.md
│ ├── README.md
│ └── component-testing.md
├── js
│ ├── README.md
│ ├── redux.md
│ ├── remove.md
│ ├── reselect.md
│ ├── redux-saga.md
│ ├── i18n.md
│ ├── immutablejs.md
│ └── routing.md
└── README.md
├── .gitignore
├── .travis.yml
├── .babelrc
├── LICENSE
├── server
├── logger.js
├── index.js
└── middlewares
│ └── frontendMiddleware.js
├── appveyor.yml
├── README.md
├── .gitattributes
├── .eslintrc.json
└── package.json
/app/translations/en.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: npm run start:prod
2 |
--------------------------------------------------------------------------------
/internals/generators/language/translations-json.hbs:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/internals/generators/language/app-locale.hbs:
--------------------------------------------------------------------------------
1 | $1 '{{language}}',
2 |
--------------------------------------------------------------------------------
/.lgtm:
--------------------------------------------------------------------------------
1 | approvals = 2
2 | pattern = "(?i):shipit:|LGTM"
3 | self_approval_off = true
4 |
--------------------------------------------------------------------------------
/internals/generators/language/add-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1addLocaleData({{language}}LocaleData);
2 |
--------------------------------------------------------------------------------
/app/containers/App/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | * AppConstants
3 | */
4 |
5 | export const DEFAULT_LOCALE = 'en';
6 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kelsonic/react-redux-material-ui-boilerplate/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/internals/generators/language/polyfill-intl-locale.hbs:
--------------------------------------------------------------------------------
1 | $1 System.import('intl/locale-data/jsonp/{{language}}.js'),
2 |
--------------------------------------------------------------------------------
/internals/generators/language/intl-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}LocaleData from 'react-intl/locale-data/{{language}}';
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = false
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/docs/general/webstorm-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kelsonic/react-redux-material-ui-boilerplate/HEAD/docs/general/webstorm-debug.png
--------------------------------------------------------------------------------
/internals/generators/language/translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}TranslationMessages from './translations/{{language}}.json';
2 |
--------------------------------------------------------------------------------
/docs/general/webstorm-eslint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kelsonic/react-redux-material-ui-boilerplate/HEAD/docs/general/webstorm-eslint.png
--------------------------------------------------------------------------------
/app/containers/HomePage/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Homepage constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'app/Homepage/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/internals/generators/language/format-translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1 {{language}}: formatTranslationMessages('{{language}}', {{language}}TranslationMessages),
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Don't check auto-generated stuff into git
2 | coverage
3 | build
4 | node_modules
5 | stats.json
6 |
7 | # Cruft
8 | .DS_Store
9 | npm-debug.log
10 | .idea
11 |
--------------------------------------------------------------------------------
/app/components/H1/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const H1 = styled.h1`
4 | font-size: 2em;
5 | margin-bottom: 0.25em;
6 | `;
7 |
8 | export default H1;
9 |
--------------------------------------------------------------------------------
/internals/generators/container/constants.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/docs/css/remove.md:
--------------------------------------------------------------------------------
1 | ## Removing `sanitize.css`
2 |
3 | Delete [lines 31 and 32 in `app.js`](../../app/app.js#L31-L32) and remove it
4 | from the `dependencies` in [`package.json`](../../package.json)!
5 |
--------------------------------------------------------------------------------
/app/containers/LanguageProvider/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * LanguageProvider constants
4 | *
5 | */
6 |
7 | export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE';
8 | export const DEFAULT_LOCALE = 'en';
9 |
--------------------------------------------------------------------------------
/app/containers/HomePage/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Homepage actions
4 | *
5 | */
6 |
7 | import {
8 | DEFAULT_ACTION,
9 | } from './constants';
10 |
11 | export function defaultAction() {
12 | return {
13 | type: DEFAULT_ACTION,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/app/components/A/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A link to a certain page, an anchor tag
3 | */
4 |
5 | import styled from 'styled-components';
6 |
7 | const A = styled.a`
8 | color: #41addd;
9 |
10 | &:hover {
11 | color: #6cc0e5;
12 | }
13 | `;
14 |
15 | export default A;
16 |
--------------------------------------------------------------------------------
/app/containers/HomePage/sagas.js:
--------------------------------------------------------------------------------
1 | // import { take, call, put, select } from 'redux-saga/effects';
2 |
3 | // Individual exports for testing
4 | export function* defaultSaga() {
5 | return;
6 | }
7 |
8 | // All sagas to be loaded
9 | export default [
10 | defaultSaga,
11 | ];
12 |
--------------------------------------------------------------------------------
/internals/generators/container/actions.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} actions
4 | *
5 | */
6 |
7 | import {
8 | DEFAULT_ACTION,
9 | } from './constants';
10 |
11 | export function defaultAction() {
12 | return {
13 | type: DEFAULT_ACTION,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/internals/scripts/helpers/checkmark.js:
--------------------------------------------------------------------------------
1 | const chalk = require('chalk');
2 |
3 | /**
4 | * Adds mark check symbol
5 | */
6 | function addCheckMark(callback) {
7 | process.stdout.write(chalk.green(' ✓'));
8 | if (callback) callback();
9 | }
10 |
11 | module.exports = addCheckMark;
12 |
--------------------------------------------------------------------------------
/internals/generators/container/sagas.js.hbs:
--------------------------------------------------------------------------------
1 | // import { take, call, put, select } from 'redux-saga/effects';
2 |
3 | // Individual exports for testing
4 | export function* defaultSaga() {
5 | return;
6 | }
7 |
8 | // All sagas to be loaded
9 | export default [
10 | defaultSaga,
11 | ];
12 |
--------------------------------------------------------------------------------
/app/containers/NotFoundPage/view.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { FormattedMessage } from 'react-intl';
3 | import messages from './messages';
4 |
5 | export default function render() {
6 | return (
7 |
20 | {{else}}
21 |
22 | {{/if}}
23 | {{#if wantMessages}}
24 |
25 | {{/if}}
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/app/components/H1/tests/index.test.js:
--------------------------------------------------------------------------------
1 | import H1 from '../index';
2 |
3 | import expect from 'expect';
4 | import { shallow } from 'enzyme';
5 | import React from 'react';
6 |
7 | describe('
', () => {
8 | it('should render a prop', () => {
9 | const id = 'testId';
10 | const renderedComponent = shallow(
11 |
12 | );
13 | expect(renderedComponent.prop('id')).toEqual(id);
14 | });
15 |
16 | it('should render its text', () => {
17 | const children = 'Text';
18 | const renderedComponent = shallow(
19 |
{children}
20 | );
21 | expect(renderedComponent.contains(children)).toEqual(true);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/internals/generators/container/selectors.js.hbs:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect';
2 |
3 | /**
4 | * Direct selector to the {{ camelCase name }} state domain
5 | */
6 | const select{{ properCase name }}Domain = () => (state) => state.get('{{ camelCase name }}');
7 |
8 | /**
9 | * Other specific selectors
10 | */
11 |
12 |
13 | /**
14 | * Default selector used by {{ properCase name }}
15 | */
16 |
17 | const select{{ properCase name }} = () => createSelector(
18 | select{{ properCase name }}Domain(),
19 | (substate) => substate.toJS()
20 | );
21 |
22 | export default select{{ properCase name }};
23 | export {
24 | select{{ properCase name }}Domain,
25 | };
26 |
--------------------------------------------------------------------------------
/app/global-styles.js:
--------------------------------------------------------------------------------
1 | import { injectGlobal } from 'styled-components';
2 |
3 | /* eslint no-unused-expressions: 0 */
4 | injectGlobal`
5 | html,
6 | body {
7 | height: 100%;
8 | width: 100%;
9 | }
10 |
11 | body {
12 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
13 | }
14 |
15 | body.fontLoaded {
16 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
17 | }
18 |
19 | #app {
20 | background-color: #fafafa;
21 | min-height: 100%;
22 | min-width: 100%;
23 | }
24 |
25 | p,
26 | label {
27 | font-family: Georgia, Times, 'Times New Roman', serif;
28 | line-height: 1.5em;
29 | }
30 | `;
31 |
--------------------------------------------------------------------------------
/app/components/CardText/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardText
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class CardText extends PureComponent {
11 | static propTypes = {
12 | actAsExpander: PropTypes.bool,
13 | children: PropTypes.node,
14 | color: PropTypes.string,
15 | expandable: PropTypes.bool,
16 | style: PropTypes.object,
17 | };
18 |
19 | static defaultProps = {
20 | actAsExpander: undefined,
21 | children: undefined,
22 | color: undefined,
23 | expandable: undefined,
24 | style: undefined,
25 | };
26 | }
27 |
28 | CardText.prototype.render = view;
29 |
30 | export default CardText;
31 |
--------------------------------------------------------------------------------
/app/components/CardMedia/view.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardMedia view
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { CardMedia } from 'material-ui/Card';
9 |
10 | export default function render() {
11 | return (
12 |
22 | {this.props.children}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/app/components/CardActions/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardActions
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class CardActions extends PureComponent {
11 | static propTypes = {
12 | actAsExpander: PropTypes.bool,
13 | children: PropTypes.node,
14 | expandable: PropTypes.bool,
15 | showExpandableButton: PropTypes.bool,
16 | style: PropTypes.object,
17 | };
18 |
19 | static defaultProps = {
20 | actAsExpander: undefined,
21 | children: undefined,
22 | expandable: undefined,
23 | showExpandableButton: undefined,
24 | style: undefined,
25 | };
26 | }
27 |
28 | CardActions.prototype.render = view;
29 |
30 | export default CardActions;
31 |
--------------------------------------------------------------------------------
/docs/general/server-configs.md:
--------------------------------------------------------------------------------
1 | # Server Configurations
2 |
3 | ## Apache
4 |
5 | This boilerplate includes a `.htaccess` file that does two things:
6 |
7 | 1. Redirect all traffic to HTTPS because ServiceWorker only works for encrypted
8 | traffic.
9 | 1. Rewrite all pages (e.g. `yourdomain.com/subpage`) to `yourdomain.com/index.html`
10 | to let `react-router` take care of presenting the correct page.
11 |
12 | > Note: For performance reasons you should probably adapt it to run as a static
13 | `.conf` file (typically under `/etc/apache2/sites-enabled` or similar) so that
14 | your server doesn't have to apply its rules dynamically per request)
15 |
16 | ## Nginx
17 |
18 | Also it includes a `.nginx.conf` file that does the same on Nginx server.
19 |
--------------------------------------------------------------------------------
/app/tests/store.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test store addons
3 | */
4 |
5 | import expect from 'expect';
6 | import configureStore from '../store'; // eslint-disable-line
7 | import { browserHistory } from 'react-router';
8 |
9 | describe('configureStore', () => {
10 | let store;
11 |
12 | before(() => {
13 | store = configureStore({}, browserHistory);
14 | });
15 |
16 | describe('asyncReducers', () => {
17 | it('should contain an object for async reducers', () => {
18 | expect(typeof store.asyncReducers).toEqual('object');
19 | });
20 | });
21 |
22 | describe('runSaga', () => {
23 | it('should contain a hook for `sagaMiddleware.run`', () => {
24 | expect(typeof store.runSaga).toEqual('function');
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/internals/scripts/helpers/progress.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const readline = require('readline');
4 |
5 | /**
6 | * Adds an animated progress indicator
7 | *
8 | * @param {string} message The message to write next to the indicator
9 | * @param {number} amountOfDots The amount of dots you want to animate
10 | */
11 | function animateProgress(message, amountOfDots) {
12 | if (typeof amountOfDots !== 'number') {
13 | amountOfDots = 3;
14 | }
15 |
16 | let i = 0;
17 | return setInterval(function() {
18 | readline.cursorTo(process.stdout, 0);
19 | i = (i + 1) % (amountOfDots + 1);
20 | const dots = new Array(i + 1).join('.');
21 | process.stdout.write(message + dots);
22 | }, 500);
23 | }
24 |
25 | module.exports = animateProgress;
26 |
--------------------------------------------------------------------------------
/app/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "React Boilerplate",
3 | "icons": [
4 | {
5 | "src": "favicon.png",
6 | "sizes": "48x48",
7 | "type": "image/png",
8 | "density": 1.0
9 | },
10 | {
11 | "src": "favicon.png",
12 | "sizes": "96x96",
13 | "type": "image/png",
14 | "density": 2.0
15 | },
16 | {
17 | "src": "favicon.png",
18 | "sizes": "144x144",
19 | "type": "image/png",
20 | "density": 3.0
21 | },
22 | {
23 | "src": "favicon.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "density": 4.0
27 | }
28 | ],
29 | "start_url": "index.html",
30 | "display": "standalone",
31 | "orientation": "portrait",
32 | "background_color": "#FFFFFF"
33 | }
--------------------------------------------------------------------------------
/app/components/CardTitle/view.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardTitle view
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { CardTitle } from 'material-ui/Card';
9 |
10 | export default function render() {
11 | return (
12 |
24 | {this.props.children}
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/app/components/Badge/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Badge
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class Badge extends PureComponent {
11 | static propTypes = {
12 | badgeContent: PropTypes.node,
13 | badgeStyle: PropTypes.object,
14 | children: PropTypes.node,
15 | className: PropTypes.string,
16 | primary: PropTypes.bool,
17 | secondary: PropTypes.bool,
18 | style: PropTypes.object,
19 | };
20 |
21 | static defaultProps = {
22 | badgeContent: undefined,
23 | badgeStyle: undefined,
24 | children: undefined,
25 | className: undefined,
26 | primary: false,
27 | secondary: false,
28 | style: undefined,
29 | };
30 | }
31 |
32 | Badge.prototype.render = view;
33 |
34 | export default Badge;
35 |
--------------------------------------------------------------------------------
/app/containers/App/view.js:
--------------------------------------------------------------------------------
1 | import React, { Children } from 'react';
2 | // Refer to http://www.material-ui.com/#/get-started/server-rendering for information on material-ui's Mui.
3 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
4 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
5 | import {
6 | green100,
7 | green500,
8 | green700,
9 | } from 'material-ui/styles/colors';
10 |
11 | const muiTheme = getMuiTheme({
12 | palette: {
13 | primary1Color: green500,
14 | primary2Color: green700,
15 | primary3Color: green100,
16 | },
17 | });
18 |
19 | export default function render() {
20 | return (
21 |
22 |
23 | {Children.toArray(this.props.children)}
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/internals/scripts/pagespeed.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | process.stdin.resume();
4 | process.stdin.setEncoding('utf8');
5 |
6 | const ngrok = require('ngrok');
7 | const psi = require('psi');
8 | const chalk = require('chalk');
9 |
10 | log('\nStarting ngrok tunnel');
11 |
12 | startTunnel(runPsi);
13 |
14 | function runPsi(url) {
15 | log('\nStarting PageSpeed Insights');
16 | psi.output(url).then(function (err) {
17 | process.exit(0);
18 | });
19 | }
20 |
21 | function startTunnel(cb) {
22 | ngrok.connect(3000, function (err, url) {
23 | if (err) {
24 | log(chalk.red('\nERROR\n' + err));
25 | process.exit(0);
26 | }
27 |
28 | log('\nServing tunnel from: ' + chalk.magenta(url));
29 | cb(url);
30 | });
31 | }
32 |
33 | function log(string) {
34 | process.stdout.write(string);
35 | }
36 |
--------------------------------------------------------------------------------
/app/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Avatar
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class Avatar extends PureComponent {
11 | static propTypes = {
12 | backgroundColor: PropTypes.string,
13 | children: PropTypes.node,
14 | className: PropTypes.string,
15 | color: PropTypes.string,
16 | icon: PropTypes.node,
17 | size: PropTypes.number,
18 | src: PropTypes.string,
19 | style: PropTypes.object,
20 | };
21 |
22 | static defaultProps = {
23 | backgroundColor: undefined,
24 | children: undefined,
25 | className: undefined,
26 | color: undefined,
27 | icon: undefined,
28 | size: 40,
29 | src: undefined,
30 | style: undefined,
31 | };
32 | }
33 |
34 | Avatar.prototype.render = view;
35 |
36 | export default Avatar;
37 |
--------------------------------------------------------------------------------
/docs/general/gotchas.md:
--------------------------------------------------------------------------------
1 | # Gotchas
2 |
3 | These are some things to be aware of when using this boilerplate.
4 |
5 | ## Special images in HTML files
6 |
7 | If you specify your images in the `.html` files using the `
![]()
` tag, everything
8 | will work fine. The problem comes up if you try to include images using anything
9 | except that tag, like meta tags:
10 |
11 | ```HTML
12 |
13 | ```
14 |
15 | The webpack `html-loader` does not recognise this as an image file and will not
16 | transfer the image to the build folder. To get webpack to transfer them, you
17 | have to import them with the file loader in your JavaScript somewhere, e.g.:
18 |
19 | ```JavaScript
20 | import 'file?name=[name].[ext]!../img/yourimg.png';
21 | ```
22 |
23 | Then webpack will correctly transfer the image to the build folder.
24 |
--------------------------------------------------------------------------------
/internals/generators/container/index.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{properCase name }}
4 | *
5 | */
6 |
7 | import { {{{ component }}} } from 'react';
8 | import { connect } from 'react-redux';
9 | import view from './view';
10 | {{#if wantActionsAndReducer}}
11 | import select{{properCase name}} from './selectors';
12 | {{/if}}
13 |
14 | class {{ properCase name }} extends {{{ component }}} {}
15 |
16 | {{ properCase name }}.prototype.render = view;
17 |
18 | {{#if wantActionsAndReducer}}
19 | const mapStateToProps = select{{properCase name}}();
20 | {{/if}}
21 |
22 | function mapDispatchToProps(dispatch) {
23 | return {
24 | dispatch,
25 | };
26 | }
27 |
28 | {{#if wantActionsAndReducer}}
29 | export default connect(mapStateToProps, mapDispatchToProps)({{ properCase name }});
30 | {{else}}
31 | export default connect(null, mapDispatchToProps)({{ properCase name }});
32 | {{/if}}
33 |
--------------------------------------------------------------------------------
/app/components/Card/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Card
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class Card extends PureComponent {
11 | static propTypes = {
12 | children: PropTypes.node,
13 | containerStyle: PropTypes.object,
14 | expandable: PropTypes.bool,
15 | expanded: PropTypes.bool,
16 | initiallyExpanded: PropTypes.bool,
17 | onExpandChange: PropTypes.function,
18 | showExpandableButton: PropTypes.bool,
19 | style: PropTypes.object,
20 | };
21 |
22 | static defaultProps = {
23 | children: undefined,
24 | containerStyle: undefined,
25 | expandable: false,
26 | expanded: null,
27 | initiallyExpanded: false,
28 | onExpandChange: undefined,
29 | showExpandableButton: undefined,
30 | style: undefined,
31 | };
32 | }
33 |
34 | Card.prototype.render = view;
35 |
36 | export default Card;
37 |
--------------------------------------------------------------------------------
/app/components/CardHeader/view.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardHeader view
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { CardHeader } from 'material-ui/Card';
9 |
10 | export default function render() {
11 | return (
12 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/docs/css/sass.md:
--------------------------------------------------------------------------------
1 | # Can I use Sass with this boilerplate?
2 |
3 | Yes, although we advise against it and **do not support this**. We selected
4 | [`styled-components`](https://github.com/styled-components/styled-components)
5 | over Sass because its approach is more powerful: instead of trying to
6 | give a styling language programmatic abilities, it pulls logic and configuration
7 | out into JS where we believe those features belong.
8 |
9 | If you _really_ still want (or need) to use Sass then...
10 |
11 | 1. You will need to add a [sass-loader](https://github.com/jtangelder/sass-loader)
12 | to the loaders section in `internals/webpack/webpack.base.babel.js` so it reads something like
13 | ```javascript
14 | {
15 | test: /\.scss$/,
16 | exclude: /node_modules/,
17 | loaders: ['style', 'css', 'sass']
18 | }
19 | ```
20 |
21 | Then run `npm i -D sass-loader node-sass`
22 |
23 | ...and you should be good to go!
24 |
--------------------------------------------------------------------------------
/internals/scripts/analyze.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const shelljs = require('shelljs');
4 | const animateProgress = require('./helpers/progress');
5 | const chalk = require('chalk');
6 | const addCheckMark = require('./helpers/checkmark');
7 |
8 | const progress = animateProgress('Generating stats');
9 |
10 | // Generate stats.json file with webpack
11 | shelljs.exec(
12 | 'webpack --config internals/webpack/webpack.prod.babel.js --profile --json > stats.json',
13 | addCheckMark.bind(null, callback) // Output a checkmark on completion
14 | );
15 |
16 | // Called after webpack has finished generating the stats.json file
17 | function callback() {
18 | clearInterval(progress);
19 | process.stdout.write(
20 | '\n\nOpen ' + chalk.magenta('http://webpack.github.io/analyse/') + ' in your browser and upload the stats.json file!' +
21 | chalk.blue('\n(Tip: ' + chalk.italic('CMD + double-click') + ' the link!)\n\n')
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/internals/generators/route/routeWithReducer.hbs:
--------------------------------------------------------------------------------
1 | {
2 | path: '{{ path }}',
3 | name: '{{ camelCase component }}',
4 | getComponent(nextState, cb) {
5 | const importModules = Promise.all([
6 | System.import('containers/{{ properCase component }}/reducer'),
7 | {{#if useSagas}}
8 | System.import('containers/{{ properCase component }}/sagas'),
9 | {{/if}}
10 | System.import('containers/{{ properCase component }}'),
11 | ]);
12 |
13 | const renderRoute = loadModule(cb);
14 |
15 | importModules.then(([reducer,{{#if useSagas}} sagas,{{/if}} component]) => {
16 | injectReducer('{{ camelCase component }}', reducer.default);
17 | {{#if useSagas}}
18 | injectSagas(sagas.default);
19 | {{/if}}
20 | renderRoute(component);
21 | });
22 |
23 | importModules.catch(errorLoading);
24 | },
25 | },$1
26 |
--------------------------------------------------------------------------------
/internals/generators/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * generator/index.js
3 | *
4 | * Exports the generators so plop knows them
5 | */
6 |
7 | const fs = require('fs');
8 | const componentGenerator = require('./component/index.js');
9 | const containerGenerator = require('./container/index.js');
10 | const routeGenerator = require('./route/index.js');
11 | const languageGenerator = require('./language/index.js');
12 |
13 | module.exports = (plop) => {
14 | plop.setGenerator('component', componentGenerator);
15 | plop.setGenerator('container', containerGenerator);
16 | plop.setGenerator('route', routeGenerator);
17 | plop.setGenerator('language', languageGenerator);
18 | plop.addHelper('directory', (comp) => {
19 | try {
20 | fs.accessSync(`app/containers/${comp}`, fs.F_OK);
21 | return `containers/${comp}`;
22 | } catch (e) {
23 | return `components/${comp}`;
24 | }
25 | });
26 | plop.addHelper('curly', (object, open) => (open ? '{' : '}'));
27 | };
28 |
--------------------------------------------------------------------------------
/app/components/CardMedia/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardMedia
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class CardMedia extends PureComponent {
11 | static propTypes = {
12 | actAsExpander: PropTypes.bool,
13 | children: PropTypes.node,
14 | expandable: PropTypes.bool,
15 | mediaStyle: PropTypes.object,
16 | overlay: PropTypes.node,
17 | overlayContainerStyle: PropTypes.object,
18 | overlayContentStyle: PropTypes.object,
19 | overlayStyle: PropTypes.object,
20 | style: PropTypes.object,
21 | };
22 |
23 | static defaultProps = {
24 | actAsExpander: undefined,
25 | children: undefined,
26 | expandable: undefined,
27 | mediaStyle: undefined,
28 | overlay: undefined,
29 | overlayContainerStyle: undefined,
30 | overlayContentStyle: undefined,
31 | overlayStyle: undefined,
32 | style: undefined,
33 | };
34 | }
35 |
36 | CardMedia.prototype.render = view;
37 |
38 | export default CardMedia;
39 |
--------------------------------------------------------------------------------
/internals/generators/container/view.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{properCase name }} view
4 | *
5 | */
6 |
7 | import React from 'react';
8 | {{#if wantHeaders}}
9 | import Helmet from 'react-helmet';
10 | {{/if}}
11 | {{#if wantMessages}}
12 | import { FormattedMessage } from 'react-intl';
13 | import messages from './messages';
14 | {{/if}}
15 | {{#if wantCSS}}
16 | import styles from './styles.css';
17 | {{/if}}
18 |
19 | export default function render() {
20 | return (
21 | {{#if wantCSS}}
22 |
23 | {{else}}
24 |
25 | {{/if}}
26 | {{#if wantHeaders}}
27 |
33 | {{/if}}
34 | {{#if wantMessages}}
35 |
36 | {{/if}}
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/app/containers/LanguageProvider/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * LanguageProvider
4 | *
5 | * this component connects the redux state language locale to the
6 | * IntlProvider component and i18n messages (loaded from `app/translations`)
7 | */
8 |
9 | import { PureComponent, PropTypes } from 'react';
10 | import { connect } from 'react-redux';
11 | import { createSelector } from 'reselect';
12 | import { selectLocale } from './selectors';
13 | import view from './view';
14 |
15 | class LanguageProvider extends PureComponent {
16 | static propTypes = {
17 | locale: PropTypes.string,
18 | messages: PropTypes.object,
19 | children: PropTypes.element.isRequired,
20 | };
21 | }
22 |
23 | LanguageProvider.prototype.render = view;
24 |
25 | const mapStateToProps = createSelector(
26 | selectLocale(),
27 | (locale) => ({ locale })
28 | );
29 |
30 | function mapDispatchToProps(dispatch) {
31 | return {
32 | dispatch,
33 | };
34 | }
35 |
36 | export default connect(mapStateToProps, mapDispatchToProps)(LanguageProvider);
37 |
--------------------------------------------------------------------------------
/app/components/AppBar/view.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * AppBar view
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import AppBar from 'material-ui/AppBar';
9 |
10 | export default function render() {
11 | return (
12 |
29 | {this.props.children}
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/components/CardTitle/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardTitle
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class CardTitle extends PureComponent {
11 | static propTypes = {
12 | actAsExpander: PropTypes.bool,
13 | children: PropTypes.node,
14 | expandable: PropTypes.bool,
15 | showExpandableButton: PropTypes.bool,
16 | style: PropTypes.object,
17 | subtitle: PropTypes.node,
18 | subtitleColor: PropTypes.string,
19 | subtitleStyle: PropTypes.object,
20 | title: PropTypes.node,
21 | titleColor: PropTypes.string,
22 | titleStyle: PropTypes.object,
23 | };
24 |
25 | static defaultProps = {
26 | actAsExpander: undefined,
27 | children: undefined,
28 | expandable: undefined,
29 | showExpandableButton: undefined,
30 | style: undefined,
31 | subtitle: undefined,
32 | subtitleColor: undefined,
33 | subtitleStyle: undefined,
34 | title: undefined,
35 | titleColor: undefined,
36 | titleStyle: undefined,
37 | };
38 | }
39 |
40 | CardTitle.prototype.render = view;
41 |
42 | export default CardTitle;
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Kelson Adams
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
React.js Boilerplate
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.dll.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WEBPACK DLL GENERATOR
3 | *
4 | * This profile is used to cache webpack's module
5 | * contexts for external library and framework type
6 | * dependencies which will usually not change often enough
7 | * to warrant building them from scratch every time we use
8 | * the webpack process.
9 | */
10 |
11 | const { join } = require('path');
12 | const defaults = require('lodash/defaultsDeep');
13 | const webpack = require('webpack');
14 | const pkg = require(join(process.cwd(), 'package.json'));
15 | const dllPlugin = require('../config').dllPlugin;
16 |
17 | if (!pkg.dllPlugin) { process.exit(0); }
18 |
19 | const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
20 | const outputPath = join(process.cwd(), dllConfig.path);
21 |
22 | module.exports = require('./webpack.base.babel')({
23 | context: process.cwd(),
24 | entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
25 | devtool: 'eval',
26 | output: {
27 | filename: '[name].dll.js',
28 | path: outputPath,
29 | library: '[name]',
30 | },
31 | plugins: [
32 | new webpack.DllPlugin({ name: '[name]', path: join(outputPath, '[name].json') }), // eslint-disable-line no-new
33 | ],
34 | });
35 |
--------------------------------------------------------------------------------
/server/logger.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 | const ip = require('ip');
5 |
6 | const divider = chalk.gray('\n-----------------------------------');
7 |
8 | /**
9 | * Logger middleware, you can customize it to make messages more personal
10 | */
11 | const logger = {
12 |
13 | // Called whenever there's an error on the server we want to print
14 | error: (err) => {
15 | console.error(chalk.red(err));
16 | },
17 |
18 | // Called when express.js app starts on given port w/o errors
19 | appStarted: (port, tunnelStarted) => {
20 | console.log(`Server started ${chalk.green('✓')}`);
21 |
22 | // If the tunnel started, log that and the URL it's available at
23 | if (tunnelStarted) {
24 | console.log(`Tunnel initialised ${chalk.green('✓')}`);
25 | }
26 |
27 | console.log(`
28 | ${chalk.bold('Access URLs:')}${divider}
29 | Localhost: ${chalk.magenta(`http://localhost:${port}`)}
30 | LAN: ${chalk.magenta(`http://${ip.address()}:${port}`) +
31 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}${divider}
32 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
33 | `);
34 | },
35 | };
36 |
37 | module.exports = logger;
38 |
--------------------------------------------------------------------------------
/app/i18n.js:
--------------------------------------------------------------------------------
1 | /**
2 | * i18n.js
3 | *
4 | * This will setup the i18n language files and locale data for your app.
5 | *
6 | */
7 | import { addLocaleData } from 'react-intl';
8 | import { DEFAULT_LOCALE } from './containers/App/constants'; // eslint-disable-line
9 |
10 | import enLocaleData from 'react-intl/locale-data/en';
11 |
12 | export const appLocales = [
13 | 'en',
14 | ];
15 |
16 | import enTranslationMessages from './translations/en.json';
17 |
18 | addLocaleData(enLocaleData);
19 |
20 | export const formatTranslationMessages = (locale, messages) => {
21 | const defaultFormattedMessages = locale !== DEFAULT_LOCALE ? formatTranslationMessages(DEFAULT_LOCALE, enTranslationMessages) : {};
22 | const formattedMessages = {};
23 | const messageKeys = Object.keys(messages);
24 | for (const messageKey of messageKeys) {
25 | if (locale === DEFAULT_LOCALE) {
26 | formattedMessages[messageKey] = messages[messageKey];
27 | } else {
28 | formattedMessages[messageKey] = messages[messageKey] || defaultFormattedMessages[messageKey];
29 | }
30 | }
31 |
32 | return formattedMessages;
33 | };
34 |
35 | export const translationMessages = {
36 | en: formatTranslationMessages('en', enTranslationMessages),
37 | };
38 |
--------------------------------------------------------------------------------
/docs/testing/README.md:
--------------------------------------------------------------------------------
1 | # Testing
2 |
3 | - [Unit Testing](unit-testing.md)
4 | - [Component Testing](component-testing.md)
5 | - [Remote Testing](remote-testing.md)
6 |
7 | Testing your application is a vital part of serious development. There are a few
8 | things you should test. If you've never done this before start with [unit testing](unit-testing.md).
9 | Move on to [component testing](component-testing.md) when you feel like you
10 | understand that!
11 |
12 | We also support [remote testing](remote-testing.md) your local application,
13 | which is quite awesome, so definitely check that out!
14 |
15 | ## Usage with this boilerplate
16 |
17 | To test your application started with this boilerplate do the following:
18 |
19 | 1. Sprinkle `.test.js` files directly next to the parts of your application you
20 | want to test. (Or in `test/` subdirectories, it doesn't really matter as long
21 | as they are directly next to those parts and end in `.test.js`)
22 |
23 | 1. Write your unit and component tests in those files.
24 |
25 | 1. Run `npm run test` in your terminal and see all the tests pass! (hopefully)
26 |
27 | There are a few more commands related to testing, checkout the [commands documentation](../general/commands.md#testing)
28 | for the full list!
29 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | /* eslint consistent-return:0 */
2 |
3 | const express = require('express');
4 | const logger = require('./logger');
5 |
6 | const argv = require('minimist')(process.argv.slice(2));
7 | const setup = require('./middlewares/frontendMiddleware');
8 | const isDev = process.env.NODE_ENV !== 'production';
9 | const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
10 | const resolve = require('path').resolve;
11 | const app = express();
12 |
13 | // If you need a backend, e.g. an API, add your custom backend-specific middleware here
14 | // app.use('/api', myApi);
15 |
16 | // In production we need to pass these values in instead of relying on webpack
17 | setup(app, {
18 | outputPath: resolve(process.cwd(), 'build'),
19 | publicPath: '/',
20 | });
21 |
22 | // get the intended port number, use port 3000 if not provided
23 | const port = argv.port || process.env.PORT || 3000;
24 |
25 | // Start your app.
26 | app.listen(port, (err) => {
27 | if (err) {
28 | return logger.error(err.message);
29 | }
30 |
31 | // Connect to ngrok in dev mode
32 | if (ngrok) {
33 | ngrok.connect(port, (innerErr, url) => {
34 | if (innerErr) {
35 | return logger.error(innerErr);
36 | }
37 |
38 | logger.appStarted(port, url);
39 | });
40 | } else {
41 | logger.appStarted(port);
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # http://www.appveyor.com/docs/appveyor-yml
2 |
3 | # Set build version format here instead of in the admin panel
4 | version: "{build}"
5 |
6 | # Do not build on gh tags
7 | skip_tags: true
8 |
9 | # Test against these versions of Node.js
10 | environment:
11 |
12 | matrix:
13 | # Node versions to run
14 | - nodejs_version: 6
15 | - nodejs_version: 5
16 | - nodejs_version: 4
17 |
18 | # Fix line endings in Windows. (runs before repo cloning)
19 | init:
20 | - git config --global core.autocrlf input
21 |
22 | # Install scripts--runs after repo cloning
23 | install:
24 | # Install chrome
25 | - choco install -y googlechrome
26 | # Install the latest stable version of Node
27 | - ps: Install-Product node $env:nodejs_version
28 | - npm -g install npm
29 | - set PATH=%APPDATA%\npm;%PATH%
30 | - npm install
31 |
32 | # Disable automatic builds
33 | build: off
34 |
35 | # Post-install test scripts
36 | test_script:
37 | # Output debugging info
38 | - node --version
39 | - npm --version
40 | # run build and run tests
41 | - npm run build
42 |
43 | # Cache node_modules for faster builds
44 | cache:
45 | - node_modules -> package.json
46 |
47 | # remove, as appveyor doesn't support secure variables on pr builds
48 | # so `COVERALLS_REPO_TOKEN` cannot be set, without hard-coding in this file
49 | #on_success:
50 | #- npm run coveralls
51 |
--------------------------------------------------------------------------------
/app/components/CardHeader/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CardHeader
4 | *
5 | */
6 |
7 | import { PropTypes, PureComponent } from 'react';
8 | import view from './view';
9 |
10 | class CardHeader extends PureComponent {
11 | static propTypes = {
12 | actAsExpander: PropTypes.bool,
13 | avatar: PropTypes.node,
14 | children: PropTypes.node,
15 | closeIcon: PropTypes.node,
16 | expandable: PropTypes.bool,
17 | openIcon: PropTypes.node,
18 | showExpandableButton: PropTypes.bool,
19 | style: PropTypes.object,
20 | subtitle: PropTypes.node,
21 | subtitleColor: PropTypes.string,
22 | subtitleStyle: PropTypes.object,
23 | textStyle: PropTypes.object,
24 | title: PropTypes.node,
25 | titleColor: PropTypes.string,
26 | titleStyle: PropTypes.object,
27 | };
28 |
29 | static defaultProps = {
30 | actAsExpander: undefined,
31 | avatar: null,
32 | children: undefined,
33 | closeIcon: undefined,
34 | expandable: undefined,
35 | openIcon: undefined,
36 | showExpandableButton: undefined,
37 | style: undefined,
38 | subtitle: undefined,
39 | subtitleColor: undefined,
40 | subtitleStyle: undefined,
41 | textStyle: undefined,
42 | title: undefined,
43 | titleColor: undefined,
44 | titleStyle: undefined,
45 | };
46 | }
47 |
48 | CardHeader.prototype.render = view;
49 |
50 | export default CardHeader;
51 |
--------------------------------------------------------------------------------
/app/reducers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Combine all reducers in this file and export the combined reducers.
3 | * If we were to do this in store.js, reducers wouldn't be hot reloadable.
4 | */
5 |
6 | import { combineReducers } from 'redux-immutable';
7 | import { fromJS } from 'immutable';
8 | import { LOCATION_CHANGE } from 'react-router-redux';
9 | import languageProviderReducer from 'containers/LanguageProvider/reducer';
10 |
11 | /*
12 | * routeReducer
13 | *
14 | * The reducer merges route location changes into our immutable state.
15 | * The change is necessitated by moving to react-router-redux@4
16 | *
17 | */
18 |
19 | // Initial routing state
20 | const routeInitialState = fromJS({
21 | locationBeforeTransitions: null,
22 | });
23 |
24 | /**
25 | * Merge route into the global application state
26 | */
27 | function routeReducer(state = routeInitialState, action) {
28 | switch (action.type) {
29 | /* istanbul ignore next */
30 | case LOCATION_CHANGE:
31 | return state.merge({
32 | locationBeforeTransitions: action.payload,
33 | });
34 | default:
35 | return state;
36 | }
37 | }
38 |
39 | /**
40 | * Creates the main reducer with the asynchronously loaded ones
41 | */
42 | export default function createReducer(asyncReducers) {
43 | return combineReducers({
44 | route: routeReducer,
45 | language: languageProviderReducer,
46 | ...asyncReducers,
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/docs/js/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript
2 |
3 | ## State management
4 |
5 | This boilerplate manages application state using [Redux](redux.md), makes it
6 | immutable with [`ImmutableJS`](immutablejs.md) and keeps access performant
7 | via [`reselect`](reselect.md).
8 |
9 | For managing asynchronous flows (e.g. logging in) we use [`redux-saga`](redux-saga.md).
10 |
11 | For routing, we use [`react-router` in combination with `react-router-redux`](routing.md).
12 |
13 | We include a generator for components, containers, sagas, routes and selectors.
14 | Run `npm run generate` to choose from the available generators, and automatically
15 | add new parts of your application!
16 |
17 | > Note: If you want to skip the generator selection process,
18 | `npm run generate
` also works. (e.g. `npm run generate route`)
19 |
20 | ### Learn more
21 |
22 | - [Redux](redux.md)
23 | - [ImmutableJS](immutablejs.md)
24 | - [reselect](reselect.md)
25 | - [redux-saga](redux-saga.md)
26 | - [react-intl](i18n.md)
27 | - [routing](routing.md)
28 |
29 | ## Architecture: `components` and `containers`
30 |
31 | We adopted a split between stateless, reusable components called (wait for it...)
32 | `components` and stateful parent components called `containers`.
33 |
34 | ### Learn more
35 |
36 | See [this article](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)
37 | by Dan Abramov for a great introduction to this approach.
38 |
--------------------------------------------------------------------------------
/docs/css/styled-componets.md:
--------------------------------------------------------------------------------
1 | # `styled-components`
2 |
3 | `styled-components` allow you to write actual CSS code in your JavaScript to style your components,
4 | removing the mapping between components and styles.
5 |
6 | See the
7 | [official documentation](https://github.com/styled-components/styled-components)
8 | for more information!
9 |
10 | ## Usage
11 |
12 | This creates two react components, `` and ``:
13 |
14 | ```JSX
15 | import React from 'react';
16 |
17 | import styled from 'styled-components';
18 |
19 | // Create a react component that renders an