├── internals ├── templates │ ├── translations │ │ └── en.json │ ├── styles.css │ ├── languageProvider │ │ ├── constants.js │ │ ├── actions.js │ │ ├── selectors.js │ │ ├── reducer.js │ │ └── languageProvider.js │ ├── homePage │ │ ├── messages.js │ │ └── homePage.js │ ├── notFoundPage │ │ ├── messages.js │ │ └── notFoundPage.js │ ├── selectors.test.js │ ├── selectors.js │ ├── homePage.js │ ├── notFoundPage.js │ ├── store.test.js │ ├── i18n.js │ ├── index.html │ ├── appContainer.js │ ├── reducers.js │ ├── routes.js │ ├── store.js │ └── asyncInjectors.js ├── generators │ ├── language │ │ ├── translations-json.hbs │ │ ├── app-locale.hbs │ │ ├── add-locale-data.hbs │ │ ├── polyfill-intl-locale.hbs │ │ ├── intl-locale-data.hbs │ │ ├── format-translation-messages.hbs │ │ ├── translation-messages.hbs │ │ └── index.js │ ├── component │ │ ├── styles.css.hbs │ │ ├── test.js.hbs │ │ ├── messages.js.hbs │ │ ├── stateless.js.hbs │ │ ├── es6.js.hbs │ │ └── index.js │ ├── container │ │ ├── styles.css.hbs │ │ ├── constants.js.hbs │ │ ├── actions.js.hbs │ │ ├── sagas.js.hbs │ │ ├── test.js.hbs │ │ ├── reducer.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 │ ├── route │ │ ├── route.hbs │ │ ├── routeWithReducer.hbs │ │ └── index.js │ ├── utils │ │ └── componentExists.js │ └── index.js ├── scripts │ ├── helpers │ │ ├── checkmark.js │ │ └── progress.js │ ├── npmcheckversion.js │ ├── pagespeed.js │ ├── analyze.js │ ├── dependencies.js │ ├── setup.js │ └── clean.js ├── testing │ ├── test-bundler.js │ └── karma.conf.js ├── webpack │ └── webpack.dll.babel.js └── config.js ├── app ├── components │ ├── H2 │ │ ├── styles.module.css │ │ ├── package.json │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── Toggle │ │ ├── styles.module.css │ │ ├── package.json │ │ ├── tests │ │ │ └── index.test.tsx │ │ └── index.tsx │ ├── H1 │ │ ├── styles.module.css │ │ ├── package.json │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── A │ │ ├── styles.module.css │ │ ├── package.json │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── H3 │ │ ├── package.json │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── Img │ │ ├── package.json │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── List │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── tests │ │ │ └── index.test.tsx │ │ └── index.tsx │ ├── Button │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── Footer │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── IssueIcon │ │ ├── package.json │ │ ├── tests │ │ │ └── index.test.tsx │ │ └── index.tsx │ ├── ListItem │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── ToggleOption │ │ ├── package.json │ │ ├── tests │ │ │ └── index.test.tsx │ │ └── index.tsx │ └── LoadingIndicator │ │ ├── package.json │ │ ├── tests │ │ └── index.test.tsx │ │ ├── index.tsx │ │ └── styles.module.css ├── containers │ ├── LocaleToggle │ │ ├── styles.module.css │ │ ├── package.json │ │ ├── messages.tsx │ │ ├── tests │ │ │ ├── messages.test.ts │ │ │ └── index.test.tsx │ │ └── index.tsx │ ├── App │ │ ├── banner-metal.jpg │ │ ├── package.json │ │ ├── constants.ts │ │ ├── styles.module.css │ │ ├── tests │ │ │ ├── index.test.tsx │ │ │ ├── actions.test.ts │ │ │ ├── reducer.test.ts │ │ │ └── selectors.test.ts │ │ ├── selectors.ts │ │ ├── index.tsx │ │ ├── reducer.ts │ │ └── actions.ts │ ├── FeaturePage │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── tests │ │ │ └── index.test.tsx │ │ └── index.tsx │ ├── HomePage │ │ ├── package.json │ │ ├── selectors.ts │ │ ├── constants.ts │ │ ├── tests │ │ │ ├── actions.test.ts │ │ │ ├── reducer.test.ts │ │ │ └── selectors.test.ts │ │ ├── styles.module.css │ │ ├── reducer.ts │ │ ├── actions.ts │ │ ├── messages.ts │ │ └── sagas.ts │ ├── NotFoundPage │ │ ├── package.json │ │ ├── messages.ts │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ ├── RepoListItem │ │ ├── package.json │ │ ├── styles.module.css │ │ ├── index.tsx │ │ └── tests │ │ │ └── index.test.tsx │ └── LanguageProvider │ │ ├── package.json │ │ ├── constants.tsx │ │ ├── actions.tsx │ │ ├── selectors.tsx │ │ ├── tests │ │ ├── selectors.test.ts │ │ ├── actions.test.ts │ │ ├── reducer.test.ts │ │ └── index.test.tsx │ │ ├── reducer.tsx │ │ └── index.tsx ├── favicon.ico ├── utils │ ├── tests │ │ ├── stubs.d.ts │ │ └── request.test.ts │ ├── request.ts │ └── asyncInjectors.ts ├── utils.d.ts ├── tests │ └── store.test.ts ├── manifest.json ├── i18n.ts ├── index.html ├── .nginx.conf ├── .htaccess ├── reducers.ts ├── store.ts └── routes.ts ├── typings ├── tslint.json └── untyped │ └── index.d.ts ├── .editorconfig ├── docs ├── general │ ├── webstorm-debug.png │ ├── webstorm-eslint.png │ ├── server-configs.md │ ├── gotchas.md │ ├── deployment.md │ ├── files.md │ └── remove.md ├── css │ ├── stylelint.md │ ├── README.md │ ├── sanitize.md │ ├── remove.md │ ├── css-modules.md │ ├── postcss.md │ └── sass.md ├── testing │ ├── remote-testing.md │ └── README.md └── js │ ├── remove.md │ ├── README.md │ ├── redux.md │ ├── reselect.md │ ├── routing.md │ ├── redux-saga.md │ └── immutablejs.md ├── .gitignore ├── .travis.yml ├── tslint.json ├── tsconfig.json ├── LICENSE.md ├── appveyor.yml ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── server ├── logger.ts ├── index.ts └── middlewares │ └── frontendMiddleware.ts ├── .gitattributes └── CODE_OF_CONDUCT.md /internals/templates/translations/en.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /internals/generators/language/translations-json.hbs: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /internals/generators/language/app-locale.hbs: -------------------------------------------------------------------------------- 1 | $1 2 | '{{language}}', 3 | -------------------------------------------------------------------------------- /app/components/H2/styles.module.css: -------------------------------------------------------------------------------- 1 | .heading2 { 2 | font-size: 1.5em; 3 | } 4 | -------------------------------------------------------------------------------- /app/containers/LocaleToggle/styles.module.css: -------------------------------------------------------------------------------- 1 | .localeToggle { 2 | padding: 2px; 3 | } 4 | -------------------------------------------------------------------------------- /internals/generators/language/add-locale-data.hbs: -------------------------------------------------------------------------------- 1 | $1addLocaleData({{language}}LocaleData); 2 | -------------------------------------------------------------------------------- /typings/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "note": "dummy file to suppress errors on intellij products" 3 | } 4 | -------------------------------------------------------------------------------- /app/components/Toggle/styles.module.css: -------------------------------------------------------------------------------- 1 | .toggle { 2 | line-height: 1em; 3 | height: 20px; 4 | } 5 | -------------------------------------------------------------------------------- /app/components/H1/styles.module.css: -------------------------------------------------------------------------------- 1 | .heading1 { 2 | font-size: 2em; 3 | margin-bottom: 0.25em; 4 | } 5 | -------------------------------------------------------------------------------- /internals/generators/component/styles.css.hbs: -------------------------------------------------------------------------------- 1 | .{{ camelCase name }} { /* stylelint-disable */ 2 | 3 | } 4 | -------------------------------------------------------------------------------- /internals/generators/container/styles.css.hbs: -------------------------------------------------------------------------------- 1 | .{{ camelCase name }} { /* stylelint-disable */ 2 | 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikeForceZero/react-typescript-boilerplate/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /internals/generators/language/polyfill-intl-locale.hbs: -------------------------------------------------------------------------------- 1 | $1 System.import('intl/locale-data/jsonp/{{language}}.js'), 2 | -------------------------------------------------------------------------------- /app/components/A/styles.module.css: -------------------------------------------------------------------------------- 1 | .link { 2 | color: #41ADDD; 3 | } 4 | 5 | .link:hover { 6 | color: #6CC0E5; 7 | } 8 | -------------------------------------------------------------------------------- /internals/generators/language/intl-locale-data.hbs: -------------------------------------------------------------------------------- 1 | $1import {{language}}LocaleData from 'react-intl/locale-data/{{language}}'; 2 | -------------------------------------------------------------------------------- /app/components/A/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "A", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/components/H1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "H1", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/H2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "H2", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/H3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "H3", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/Img/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Img", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/List/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "List", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /docs/general/webstorm-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikeForceZero/react-typescript-boilerplate/HEAD/docs/general/webstorm-debug.png -------------------------------------------------------------------------------- /internals/generators/language/format-translation-messages.hbs: -------------------------------------------------------------------------------- 1 | $1 {{language}}: formatTranslationMessages({{language}}TranslationMessages), 2 | -------------------------------------------------------------------------------- /internals/generators/language/translation-messages.hbs: -------------------------------------------------------------------------------- 1 | $1import {{language}}TranslationMessages from './translations/{{language}}.json'; 2 | -------------------------------------------------------------------------------- /app/components/Button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Button", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/Footer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Footer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/Toggle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Toggle", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /docs/general/webstorm-eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikeForceZero/react-typescript-boilerplate/HEAD/docs/general/webstorm-eslint.png -------------------------------------------------------------------------------- /app/components/IssueIcon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IssueIcon", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/ListItem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ListItem", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/App/banner-metal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikeForceZero/react-typescript-boilerplate/HEAD/app/containers/App/banner-metal.jpg -------------------------------------------------------------------------------- /app/containers/App/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FeaturePage", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/FeaturePage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/HomePage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HomePage", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/utils/tests/stubs.d.ts: -------------------------------------------------------------------------------- 1 | import Sinon from 'sinon'; 2 | 3 | export interface IStubedWindow { 4 | fetch: Sinon.SinonStub & typeof window.fetch; 5 | } 6 | -------------------------------------------------------------------------------- /app/components/ToggleOption/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ToggleOption", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/LocaleToggle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LocaleToggle", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NotFoundPage", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/RepoListItem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RepoListItem", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /internals/templates/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * styles.css 3 | * 4 | * App container styles 5 | */ 6 | 7 | .container { 8 | display: block; 9 | } 10 | -------------------------------------------------------------------------------- /app/components/LoadingIndicator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LoadingIndicator", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LanguageProvider", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./index.tsx" 6 | } 7 | -------------------------------------------------------------------------------- /app/components/Footer/styles.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | display: flex; 3 | justify-content: space-between; 4 | padding: 3em 0; 5 | border-top: 1px solid #666; 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/constants.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider constants 4 | * 5 | */ 6 | 7 | export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE'; 8 | -------------------------------------------------------------------------------- /internals/templates/languageProvider/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider constants 4 | * 5 | */ 6 | 7 | export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE'; 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/utils.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css' { 2 | const content: string|any; 3 | export default content; 4 | } 5 | 6 | declare module '*!raw' { 7 | const content: string|any; 8 | export default content; 9 | } 10 | -------------------------------------------------------------------------------- /app/components/H3/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | class H3 extends React.Component<{}, {}> { 4 | public render() { 5 | return ( 6 |

7 | ); 8 | } 9 | } 10 | 11 | export default H3; 12 | -------------------------------------------------------------------------------- /app/containers/FeaturePage/styles.module.css: -------------------------------------------------------------------------------- 1 | .list { 2 | font-family: Georgia, Times, 'Times New Roman', serif; 3 | padding-left: 1.75em; 4 | } 5 | 6 | .listItem { 7 | margin: 1em 0; 8 | } 9 | 10 | .listItemTitle { 11 | font-weight: bold; 12 | } 13 | -------------------------------------------------------------------------------- /internals/scripts/helpers/checkmark.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk'); 2 | 3 | /** 4 | * Adds mark check symbol 5 | */ 6 | function addCheckMark(callback) { 7 | process.stdout.write(chalk.green(' ✓')); 8 | callback(); 9 | } 10 | 11 | module.exports = addCheckMark; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | build 4 | node_modules 5 | !typings 6 | typings/* 7 | !typings/tslint.json 8 | !/typings/fixed/ 9 | !/typings/untyped/ 10 | stats.json 11 | 12 | # Cruft 13 | .DS_Store 14 | npm-debug.log 15 | .idea 16 | .awcache 17 | -------------------------------------------------------------------------------- /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/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/LanguageProvider/actions.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider actions 4 | * 5 | */ 6 | 7 | import { 8 | CHANGE_LOCALE, 9 | } from './constants'; 10 | 11 | export function changeLocale(languageLocale) { 12 | return { 13 | type: CHANGE_LOCALE, 14 | locale: languageLocale, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /internals/generators/route/route.hbs: -------------------------------------------------------------------------------- 1 | { 2 | path: '{{ path }}', 3 | name: '{{ camelCase component }}', 4 | getComponent(location, cb) { 5 | System.import('{{{directory (properCase component)}}}') 6 | .then(loadModule(cb)) 7 | .catch(errorLoading); 8 | }, 9 | },$1 10 | -------------------------------------------------------------------------------- /internals/templates/languageProvider/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider actions 4 | * 5 | */ 6 | 7 | import { 8 | CHANGE_LOCALE, 9 | } from './constants'; 10 | 11 | export function changeLocale(languageLocale) { 12 | return { 13 | type: CHANGE_LOCALE, 14 | locale: languageLocale, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /app/components/H1/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const styles = require('./styles.module.css'); 4 | 5 | class H1 extends React.Component<{}, {}> { 6 | public render() { 7 | return ( 8 |

9 | ); 10 | } 11 | } 12 | 13 | export default H1; 14 | -------------------------------------------------------------------------------- /app/components/H2/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const styles = require('./styles.module.css'); 4 | 5 | class H2 extends React.Component<{}, {}> { 6 | public render() { 7 | return( 8 |

9 | ); 10 | } 11 | 12 | } 13 | 14 | export default H2; 15 | -------------------------------------------------------------------------------- /internals/scripts/npmcheckversion.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var exec = require('child_process').exec; 3 | exec('npm -v', function (err, stdout, stderr) { 4 | if (err) throw err; 5 | if (parseFloat(stdout) < 3) { 6 | throw new Error('[ERROR: React Boilerplate] You need npm version @>=3'); 7 | process.exit(1); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /internals/generators/component/test.js.hbs: -------------------------------------------------------------------------------- 1 | // import {{ properCase name }} from '../index'; 2 | 3 | import expect from 'expect'; 4 | // import { shallow } from 'enzyme'; 5 | // import React from 'react'; 6 | 7 | describe('<{{ properCase name }} />', () => { 8 | it('Expect to have unit tests specified', () => { 9 | expect(true).toEqual(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /internals/generators/container/test.js.hbs: -------------------------------------------------------------------------------- 1 | // import {{ properCase name }} from '../index'; 2 | 3 | import expect from 'expect'; 4 | // import { shallow } from 'enzyme'; 5 | // import React from 'react'; 6 | 7 | describe('<{{ properCase name }} />', () => { 8 | it('Expect to have unit tests specified', () => { 9 | expect(true).toEqual(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /internals/generators/container/reducer.test.js.hbs: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import {{ camelCase name }}Reducer from '../reducer'; 3 | import { fromJS } from 'immutable'; 4 | 5 | describe('{{ camelCase name }}Reducer', () => { 6 | it('returns the initial state', () => { 7 | expect({{ camelCase name }}Reducer(undefined, {})).toEqual(fromJS({})); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /internals/templates/homePage/messages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * HomePage Messages 3 | * 4 | * This contains all the text for the HomePage component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export default defineMessages({ 9 | header: { 10 | id: 'app.components.HomePage.header', 11 | defaultMessage: 'This is HomePage components !', 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /app/containers/HomePage/selectors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Homepage selectors 3 | */ 4 | 5 | import { createSelector } from 'reselect'; 6 | 7 | const selectHome = () => (state) => state.get('home'); 8 | 9 | const selectUsername = () => createSelector( 10 | selectHome(), 11 | (homeState) => homeState.get('username'), 12 | ); 13 | 14 | export { 15 | selectHome, 16 | selectUsername, 17 | }; 18 | -------------------------------------------------------------------------------- /internals/templates/notFoundPage/messages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NotFoundPage Messages 3 | * 4 | * This contains all the text for the NotFoundPage component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export default defineMessages({ 9 | header: { 10 | id: 'app.components.NotFoundPage.header', 11 | defaultMessage: 'This is NotFoundPage component !', 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /app/components/ListItem/styles.module.css: -------------------------------------------------------------------------------- 1 | .item { 2 | width: 100%; 3 | height: 3em; 4 | display: flex; 5 | align-items: center; 6 | position: relative; 7 | } 8 | 9 | .item + .item { 10 | border-top: 1px solid #EEE; 11 | } 12 | 13 | .itemContent { 14 | display: flex; 15 | justify-content: space-between; 16 | width: 100%; 17 | height: 100%; 18 | align-items: center; 19 | } 20 | -------------------------------------------------------------------------------- /internals/generators/container/messages.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * {{properCase name }} Messages 3 | * 4 | * This contains all the text for the {{properCase name }} component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export default defineMessages({ 9 | header: { 10 | id: 'app.containers.{{properCase name }}.header', 11 | defaultMessage: 'This is {{properCase name}} container !', 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /docs/css/stylelint.md: -------------------------------------------------------------------------------- 1 | # stylelint 2 | 3 | stylelint catches bugs and helps keep you and your team on consistent with the 4 | standards and conventions you define. 5 | 6 | We've pre-configured it to extend [stylelint-config-standard](https://github.com/stylelint/stylelint-config-standard) 7 | but you can (and should!) adapt it to your house style. 8 | 9 | See the [official documentation](http://stylelint.io/) for more information! 10 | -------------------------------------------------------------------------------- /internals/generators/component/messages.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * {{ properCase name }} Messages 3 | * 4 | * This contains all the text for the {{ properCase name }} component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export default defineMessages({ 9 | header: { 10 | id: 'app.components.{{ properCase name }}.header', 11 | defaultMessage: 'This is the {{ properCase name}} component !', 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /internals/generators/container/sagas.test.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * Test sagas 3 | */ 4 | 5 | import expect from 'expect'; 6 | // import { take, call, put, select } from 'redux-saga/effects'; 7 | // import { defaultSaga } from '../sagas'; 8 | 9 | // const generator = defaultSaga(); 10 | 11 | describe('defaultSaga Saga', () => { 12 | it('Expect to have unit tests specified', () => { 13 | expect(true).toEqual(false); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /internals/generators/container/selectors.test.js.hbs: -------------------------------------------------------------------------------- 1 | // import { select{{ properCase name }}Domain } from '../selectors'; 2 | // import { fromJS } from 'immutable'; 3 | import expect from 'expect'; 4 | 5 | // const selector = select{{ properCase name}}Domain(); 6 | 7 | describe('select{{ properCase name }}Domain', () => { 8 | it('Expect to have unit tests specified', () => { 9 | expect('Test case').toEqual(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /app/components/List/styles.module.css: -------------------------------------------------------------------------------- 1 | /* Gives the scrollbar rounded edges via overflow: hidden */ 2 | .listWrapper { 3 | padding: 0; 4 | margin: 0; 5 | width: 100%; 6 | background-color: white; 7 | border: 1px solid #CCC; 8 | border-radius: 3px; 9 | overflow: hidden; 10 | } 11 | 12 | .list { 13 | list-style: none; 14 | margin: 0; 15 | width: 100%; 16 | max-height: 30em; 17 | overflow-y: auto; 18 | padding: 0 1em; 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 7 5 | - 6 6 | 7 | script: 8 | - npm run build 9 | - npm run test 10 | 11 | before_install: 12 | - export CHROME_BIN=chromium-browser 13 | - export DISPLAY=:99.0 14 | - sh -e /etc/init.d/xvfb start 15 | 16 | notifications: 17 | email: 18 | on_failure: change 19 | 20 | after_success: 'npm run coveralls' 21 | 22 | cache: 23 | yarn: true 24 | directories: 25 | - node_modules 26 | -------------------------------------------------------------------------------- /app/components/IssueIcon/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import IssueIcon from 'app/components/IssueIcon'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | 7 | describe('', () => { 8 | it('should render a SVG', () => { 9 | const renderedComponent = shallow( 10 | , 11 | ); 12 | expect(renderedComponent.find('svg').length).toEqual(1); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /docs/testing/remote-testing.md: -------------------------------------------------------------------------------- 1 | # Remote testing 2 | 3 | ```Shell 4 | npm run start:tunnel 5 | ``` 6 | 7 | This command will start a server and tunnel it with `ngrok`. You'll get a URL 8 | that looks a bit like this: `http://abcdef.ngrok.com` 9 | 10 | This URL will show the version of your application that's in the `build` folder, 11 | and it's accessible from the entire world! This is great for testing on different 12 | devices and from different locations! 13 | -------------------------------------------------------------------------------- /docs/css/README.md: -------------------------------------------------------------------------------- 1 | # CSS 2 | 3 | This boilerplate uses PostCSS as a CSS preprocessor with a few utility plugins 4 | to make it "batteries included". 5 | 6 | CSS Modules lets us embrace component encapsulation while sanitize.css gives us 7 | data-driven cross-browser normalisation. 8 | 9 | Learn more: 10 | 11 | - [PostCSS](postcss.md) 12 | - [CSS Modules](css-modules.md) 13 | - [sanitize.css](sanitize.md) 14 | - [stylelint.css](stylelint.md) 15 | - [Using Sass](sass.md) 16 | -------------------------------------------------------------------------------- /app/components/LoadingIndicator/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import LoadingIndicator from 'app/components/LoadingIndicator'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | 7 | describe('', () => { 8 | it('should render 14 divs', () => { 9 | const renderedComponent = shallow( 10 | , 11 | ); 12 | expect(renderedComponent.find('div').length).toEqual(14); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/selectors.tsx: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | /** 4 | * Direct selector to the languageToggle state domain 5 | */ 6 | const selectLanguage = () => (state) => state.get('language'); 7 | 8 | /** 9 | * Select the language locale 10 | */ 11 | 12 | const selectLocale = () => createSelector( 13 | selectLanguage(), 14 | (languageState) => languageState.get('locale'), 15 | ); 16 | 17 | export { 18 | selectLanguage, 19 | selectLocale, 20 | }; 21 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/messages.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * NotFoundPage Messages 3 | * 4 | * This contains all the text for the NotFoundPage component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export default defineMessages({ 9 | header: { 10 | id: 'boilerplate.containers.NotFoundPage.header', 11 | defaultMessage: 'Page not found.', 12 | }, 13 | homeButton: { 14 | id: 'boilerplate.containers.NotFoundPage.home', 15 | defaultMessage: 'Home', 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /internals/templates/languageProvider/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | /** 4 | * Direct selector to the languageToggle state domain 5 | */ 6 | const selectLanguage = () => (state) => state.get('language'); 7 | 8 | /** 9 | * Select the language locale 10 | */ 11 | 12 | const selectLocale = () => createSelector( 13 | selectLanguage(), 14 | (languageState) => languageState.get('locale') 15 | ); 16 | 17 | export { 18 | selectLanguage, 19 | selectLocale, 20 | }; 21 | -------------------------------------------------------------------------------- /internals/generators/container/actions.test.js.hbs: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | defaultAction, 4 | } from '../actions'; 5 | import { 6 | DEFAULT_ACTION, 7 | } from '../constants'; 8 | 9 | describe('{{ properCase name }} actions', () => { 10 | describe('Default Action', () => { 11 | it('has a type of DEFAULT_ACTION', () => { 12 | const expected = { 13 | type: DEFAULT_ACTION, 14 | }; 15 | expect(defaultAction()).toEqual(expected); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/components/H3/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import H3 from '../index'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | import ReactElement = React.ReactElement; 7 | 8 | describe('

', () => { 9 | it('should render its text', () => { 10 | const children = 'Text' as any as ReactElement; 11 | const renderedComponent = shallow( 12 |

{children}

, 13 | ); 14 | expect(renderedComponent.contains(children)); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /app/containers/RepoListItem/styles.module.css: -------------------------------------------------------------------------------- 1 | .linkWrapper { 2 | width: 100%; 3 | height: 100%; 4 | display: flex; 5 | align-items: space-between; 6 | } 7 | 8 | .linkRepo { 9 | height: 100%; 10 | color: black; 11 | display: flex; 12 | align-items: center; 13 | width: 100%; 14 | } 15 | 16 | .linkIssues { 17 | color: black; 18 | height: 100%; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | } 23 | 24 | .issueIcon { 25 | fill: #CCC; 26 | margin-right: 0.25em; 27 | } 28 | -------------------------------------------------------------------------------- /internals/generators/container/reducer.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * {{ properCase name }} reducer 4 | * 5 | */ 6 | 7 | import { fromJS } from 'immutable'; 8 | import { 9 | DEFAULT_ACTION, 10 | } from './constants'; 11 | 12 | const initialState = fromJS({}); 13 | 14 | function {{ camelCase name }}Reducer(state = initialState, action) { 15 | switch (action.type) { 16 | case DEFAULT_ACTION: 17 | return state; 18 | default: 19 | return state; 20 | } 21 | } 22 | 23 | export default {{ camelCase name }}Reducer; 24 | -------------------------------------------------------------------------------- /app/components/H1/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import H1 from 'app/components/H1'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | import ReactElement = React.ReactElement; 7 | 8 | describe('

', () => { 9 | it('should render its text', () => { 10 | const children = 'Text' as any as ReactElement; 11 | const renderedComponent = shallow( 12 |

{children}

, 13 | ); 14 | expect(renderedComponent.contains(children)); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /app/components/H2/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import H2 from 'app/components/H2'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | import ReactElement = React.ReactElement; 7 | 8 | describe('

', () => { 9 | it('should render its text', () => { 10 | const children = 'Text' as any as ReactElement; 11 | const renderedComponent = shallow( 12 |

{children}

, 13 | ); 14 | expect(renderedComponent.contains(children)); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /internals/generators/utils/componentExists.js: -------------------------------------------------------------------------------- 1 | /** 2 | * componentExists 3 | * 4 | * Check whether the given component exist in either the components or containers directory 5 | */ 6 | 7 | const fs = require('fs'); 8 | const pageComponents = fs.readdirSync('app/components'); 9 | const pageContainers = fs.readdirSync('app/containers'); 10 | const components = pageComponents.concat(pageContainers); 11 | 12 | function componentExists(comp) { 13 | return components.indexOf(comp) >= 0; 14 | } 15 | 16 | module.exports = componentExists; 17 | -------------------------------------------------------------------------------- /internals/templates/selectors.test.js: -------------------------------------------------------------------------------- 1 | import { fromJS } from 'immutable'; 2 | import expect from 'expect'; 3 | 4 | import { selectLocationState } from 'containers/App/selectors'; 5 | 6 | describe('selectLocationState', () => { 7 | it('should select the route as a plain JS object', () => { 8 | const route = fromJS({ 9 | locationBeforeTransitions: null, 10 | }); 11 | const mockedState = fromJS({ 12 | route, 13 | }); 14 | expect(selectLocationState()(mockedState)).toEqual(route.toJS()); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/selectors.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | selectLanguage, 3 | } from '../selectors'; 4 | import { fromJS } from 'immutable'; 5 | import * as expect from 'expect'; 6 | 7 | describe('selectLanguage', () => { 8 | const globalSelector = selectLanguage(); 9 | it('should select the global state', () => { 10 | const globalState = fromJS({}); 11 | const mockedState = fromJS({ 12 | language: globalState, 13 | }); 14 | expect(globalSelector(mockedState)).toEqual(globalState); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/actions.test.ts: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | import { 3 | changeLocale, 4 | } from '../actions'; 5 | import { 6 | CHANGE_LOCALE, 7 | } from '../constants'; 8 | 9 | describe('LanguageProvider actions', () => { 10 | describe('Change Local Action', () => { 11 | it('has a type of CHANGE_LOCALE', () => { 12 | const expected = { 13 | type: CHANGE_LOCALE, 14 | locale: 'de', 15 | }; 16 | expect(changeLocale('de')).toEqual(expected); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /app/components/ListItem/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const styles = require('./styles.module.css'); 4 | 5 | interface IListItemProps { 6 | className?: string; 7 | item?: any; 8 | } 9 | 10 | class ListItem extends React.Component { 11 | public render() { 12 | return ( 13 |
  • 14 |
    15 | {this.props.item} 16 |
    17 |
  • 18 | ); 19 | } 20 | } 21 | 22 | export default ListItem; 23 | -------------------------------------------------------------------------------- /app/components/A/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * A link to a certain page, an anchor tag 3 | */ 4 | 5 | import * as React from 'react'; 6 | 7 | const styles = require('./styles.module.css'); 8 | 9 | interface IAProps { 10 | className?: string; 11 | href?: string; 12 | target?: string; 13 | children?: React.ReactNode; 14 | } 15 | 16 | class A extends React.Component { 17 | public render() { 18 | return( 19 | 20 | ); 21 | } 22 | } 23 | 24 | 25 | export default A; 26 | -------------------------------------------------------------------------------- /internals/templates/selectors.js: -------------------------------------------------------------------------------- 1 | // selectLocationState expects a plain JS object for the routing state 2 | const selectLocationState = () => { 3 | let prevRoutingState; 4 | let prevRoutingStateJS; 5 | 6 | return (state) => { 7 | const routingState = state.get('route'); // or state.route 8 | 9 | if (!routingState.equals(prevRoutingState)) { 10 | prevRoutingState = routingState; 11 | prevRoutingStateJS = routingState.toJS(); 12 | } 13 | 14 | return prevRoutingStateJS; 15 | }; 16 | }; 17 | 18 | export { 19 | selectLocationState, 20 | }; 21 | -------------------------------------------------------------------------------- /app/containers/HomePage/constants.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * HomeConstants 3 | * Each action has a corresponding type, which the reducer knows and picks up on. 4 | * To avoid weird typos between the reducer and the actions, we save them as 5 | * constants here. We prefix them with 'yourproject/YourComponent' so we avoid 6 | * reducers accidentally picking up actions they shouldn't. 7 | * 8 | * Follow this format: 9 | * export const YOUR_ACTION_CONSTANT = 'yourproject/YourContainer/YOUR_ACTION_CONSTANT'; 10 | */ 11 | 12 | export const CHANGE_USERNAME = 'boilerplate/Home/CHANGE_USERNAME'; 13 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/reducer.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider reducer 4 | * 5 | */ 6 | 7 | import { fromJS } from 'immutable'; 8 | import { 9 | CHANGE_LOCALE, 10 | } from './constants'; 11 | 12 | const initialState = fromJS({ 13 | locale: 'en', 14 | }); 15 | 16 | function languageProviderReducer(state = initialState, action) { 17 | switch (action.type) { 18 | case CHANGE_LOCALE: 19 | return state 20 | .set('locale', action.locale); 21 | default: 22 | return state; 23 | } 24 | } 25 | 26 | export default languageProviderReducer; 27 | -------------------------------------------------------------------------------- /internals/templates/languageProvider/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider reducer 4 | * 5 | */ 6 | 7 | import { fromJS } from 'immutable'; 8 | import { 9 | CHANGE_LOCALE, 10 | } from './constants'; 11 | 12 | const initialState = fromJS({ 13 | locale: 'en', 14 | }); 15 | 16 | function languageProviderReducer(state = initialState, action) { 17 | switch (action.type) { 18 | case CHANGE_LOCALE: 19 | return state 20 | .set('locale', action.locale); 21 | default: 22 | return state; 23 | } 24 | } 25 | 26 | export default languageProviderReducer; 27 | -------------------------------------------------------------------------------- /internals/testing/test-bundler.js: -------------------------------------------------------------------------------- 1 | // needed for regenerator-runtime 2 | // (ES7 generator support is required by redux-saga) 3 | import 'babel-polyfill'; 4 | 5 | // If we need to use Chai, we'll have already chaiEnzyme loaded 6 | import chai from 'chai'; 7 | import chaiEnzyme from 'chai-enzyme'; 8 | chai.use(chaiEnzyme()); 9 | 10 | // Include all .(j|t)sx? files under `app`, except app.ts, reducers.ts, and routes.ts. 11 | // This is for code coverage 12 | const context = require.context('../../app', true, /^^((?!(app|reducers|routes)|.*\.d).)*\.(j|t)sx?$/); 13 | context.keys().forEach(context); 14 | -------------------------------------------------------------------------------- /app/components/Img/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Img.react.js 4 | * 5 | * Renders an image, enforcing the usage of the alt="" tag 6 | */ 7 | 8 | import * as React from 'react'; 9 | 10 | // We require the use of src and alt, only enforced by react in dev mode 11 | interface IImgProps { 12 | src: string; 13 | alt: string; 14 | className?: string; 15 | } 16 | 17 | class Img extends React.Component { 18 | public render() { 19 | return ( 20 | {this.props.alt}/ 21 | ); 22 | } 23 | } 24 | 25 | export default Img; 26 | -------------------------------------------------------------------------------- /app/containers/HomePage/tests/actions.test.ts: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | 3 | import { 4 | CHANGE_USERNAME, 5 | } from '../constants'; 6 | 7 | import { 8 | changeUsername, 9 | } from '../actions'; 10 | 11 | describe('Home Actions', () => { 12 | describe('changeUsername', () => { 13 | it('should return the correct type and the passed name', () => { 14 | const fixture = 'Max'; 15 | const expectedResult = { 16 | type: CHANGE_USERNAME, 17 | name: fixture, 18 | }; 19 | 20 | expect(changeUsername(fixture)).toEqual(expectedResult); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /app/containers/LocaleToggle/messages.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * LocaleToggle Messages 3 | * 4 | * This contains all the text for the LanguageToggle component. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | import { appLocales } from '../../i18n'; 8 | 9 | export function getLocaleMessages(locales) { 10 | return locales.reduce((messages, locale) => 11 | Object.assign(messages, { 12 | [locale]: { 13 | id: `app.components.LocaleToggle.${locale}`, 14 | defaultMessage: `${locale}`, 15 | }, 16 | }), {}); 17 | } 18 | 19 | export default defineMessages( 20 | getLocaleMessages(appLocales), 21 | ); 22 | -------------------------------------------------------------------------------- /app/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import A from 'app/components/A'; 4 | const styles = require('./styles.module.css'); 5 | 6 | class Footer extends React.Component<{}, {}> { 7 | public render() { 8 | return ( 9 | 17 | ); 18 | } 19 | } 20 | 21 | export default Footer; 22 | -------------------------------------------------------------------------------- /docs/css/sanitize.md: -------------------------------------------------------------------------------- 1 | # `sanitize.css` 2 | 3 | Sanitize.css makes browsers render elements more in 4 | line with developer expectations (e.g. having the box model set to a cascading 5 | `box-sizing: border-box`) and preferences (its defaults can be individually 6 | overridden). 7 | 8 | It was selected over older projects like `normalize.css` and `reset.css` due 9 | to its greater flexibility and better alignment with CSSNext features like CSS 10 | variables. 11 | 12 | See the [official documentation](https://github.com/10up/sanitize.css) for more 13 | information. 14 | 15 | --- 16 | 17 | _Don't like this feature? [Click here](remove.md)_ 18 | -------------------------------------------------------------------------------- /app/containers/LocaleToggle/tests/messages.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { getLocaleMessages } from '../messages'; 3 | 4 | describe('getLocaleMessages', () => { 5 | it('should create i18n messages for all locales', () => { 6 | const expected = { 7 | en: { 8 | id: 'app.components.LocaleToggle.en', 9 | defaultMessage: 'en', 10 | }, 11 | fr: { 12 | id: 'app.components.LocaleToggle.fr', 13 | defaultMessage: 'fr', 14 | }, 15 | }; 16 | 17 | const actual = getLocaleMessages(['en', 'fr']); 18 | 19 | assert.deepEqual(expected, actual); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-react"], 3 | "rules": { 4 | "indent": [ true, "spaces" ], 5 | "quotemark": [ true, "single", "jsx-double" ], 6 | "no-var-requires": false, 7 | "ordered-imports": false, 8 | "no-unused-variable": [false, "react"], 9 | "member-ordering": [false], 10 | "object-literal-sort-keys": false, 11 | "no-shadowed-variable": false, 12 | "no-console": [false], 13 | "max-line-length": [true, 180], 14 | "no-consecutive-blank-lines": [false], 15 | "no-string-literal": false, 16 | "jsx-no-multiline-js": false, 17 | "jsx-boolean-value": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/reducer.test.ts: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | import languageProviderReducer from '../reducer'; 3 | import { fromJS } from 'immutable'; 4 | import { 5 | CHANGE_LOCALE, 6 | } from '../constants'; 7 | 8 | describe('languageProviderReducer', () => { 9 | it('returns the initial state', () => { 10 | expect(languageProviderReducer(undefined, {})).toEqual(fromJS({ 11 | locale: 'en', 12 | })); 13 | }); 14 | 15 | it('changes the locale', () => { 16 | expect(languageProviderReducer(undefined, { type: CHANGE_LOCALE, locale: 'de' }).toJS()).toEqual({ 17 | locale: 'de', 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /internals/templates/homePage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * HomePage 3 | * 4 | * This is the first thing users see of our App, at the '/' route 5 | * 6 | * NOTE: while this component should technically be a stateless functional 7 | * component (SFC), hot reloading does not currently support SFCs. If hot 8 | * reloading is not a neccessity for you then you can refactor it and remove 9 | * the linting exception. 10 | */ 11 | 12 | import React from 'react'; 13 | 14 | export default class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function 15 | 16 | render() { 17 | return ( 18 |

    This is the Homepage!

    19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /internals/templates/notFoundPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NotFoundPage 3 | * 4 | * This is the page we show when the user visits a url that doesn't have a route 5 | * 6 | * NOTE: while this component should technically be a stateless functional 7 | * component (SFC), hot reloading does not currently support SFCs. If hot 8 | * reloading is not a neccessity for you then you can refactor it and remove 9 | * the linting exception. 10 | */ 11 | 12 | import React from 'react'; 13 | 14 | export default class NotFound extends React.Component { // eslint-disable-line react/prefer-stateless-function 15 | 16 | render() { 17 | return ( 18 |

    Page Not Found

    19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/components/Button/styles.module.css: -------------------------------------------------------------------------------- 1 | .buttonWrapper { 2 | width: 100%; 3 | text-align: center; 4 | margin: 4em 0; 5 | } 6 | 7 | .button { 8 | display: inline-block; 9 | box-sizing: border-box; 10 | padding: 0.25em 2em; 11 | margin: 0; 12 | text-decoration: none; 13 | border-radius: 4px; 14 | -webkit-font-smoothing: antialiased; 15 | -webkit-touch-callout: none; 16 | user-select: none; 17 | cursor: pointer; 18 | outline: 0; 19 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 20 | font-weight: bold; 21 | font-size: 16px; 22 | border: 2px solid #41ADDD; 23 | color: #41ADDD; 24 | } 25 | 26 | .button:active { 27 | background: #41ADDD; 28 | color: #FFF; 29 | } 30 | -------------------------------------------------------------------------------- /app/containers/App/constants.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * AppConstants 3 | * Each action has a corresponding type, which the reducer knows and picks up on. 4 | * To avoid weird typos between the reducer and the actions, we save them as 5 | * constants here. We prefix them with 'yourproject/YourComponent' so we avoid 6 | * reducers accidentally picking up actions they shouldn't. 7 | * 8 | * Follow this format: 9 | * export const YOUR_ACTION_CONSTANT = 'yourproject/YourContainer/YOUR_ACTION_CONSTANT'; 10 | */ 11 | 12 | export const LOAD_REPOS = 'boilerplate/App/LOAD_REPOS'; 13 | export const LOAD_REPOS_SUCCESS = 'boilerplate/App/LOAD_REPOS_SUCCESS'; 14 | export const LOAD_REPOS_ERROR = 'boilerplate/App/LOAD_REPOS_ERROR'; 15 | -------------------------------------------------------------------------------- /app/components/ListItem/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import ListItem from 'app/components/ListItem'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import * as React from 'react'; 6 | 7 | describe('', () => { 8 | it('should adopt the className', () => { 9 | const renderedComponent = shallow(); 10 | expect(renderedComponent.find('li').hasClass('test')).toEqual(true); 11 | }); 12 | 13 | it('should render the content passed to it', () => { 14 | const content = 'Hello world!'; 15 | const renderedComponent = shallow( 16 | , 17 | ); 18 | expect(renderedComponent.text()).toBe(content); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /internals/generators/component/stateless.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * {{ properCase name }} 4 | * 5 | */ 6 | 7 | import React from 'react'; 8 | 9 | {{#if wantMessages}} 10 | import { FormattedMessage } from 'react-intl'; 11 | import messages from './messages'; 12 | {{/if}} 13 | 14 | {{#if wantCSS}} 15 | import styles from './styles.css'; 16 | {{/if}} 17 | 18 | function {{ properCase name }}() { 19 | return ( 20 | {{#if wantCSS}} 21 |
    22 | {{else}} 23 |
    24 | {{/if}} 25 | {{#if wantMessages}} 26 | 27 | {{/if}} 28 |
    29 | ); 30 | } 31 | 32 | export default {{ properCase name }}; 33 | -------------------------------------------------------------------------------- /internals/scripts/helpers/progress.js: -------------------------------------------------------------------------------- 1 | var readline = require('readline'); 2 | 3 | /** 4 | * Adds an animated progress indicator 5 | * 6 | * @param {string} message The message to write next to the indicator 7 | * @param {number} amountOfDots The amount of dots you want to animate 8 | */ 9 | function animateProgress(message, amountOfDots) { 10 | if (typeof amountOfDots !== 'number') { 11 | amountOfDots = 3; 12 | } 13 | 14 | var i = 0; 15 | return setInterval(function () { 16 | readline.cursorTo(process.stdout, 0); 17 | i = (i + 1) % (amountOfDots + 1); 18 | var dots = new Array(i + 1).join('.'); 19 | process.stdout.write(message + dots); 20 | }, 500); 21 | } 22 | 23 | module.exports = animateProgress; 24 | -------------------------------------------------------------------------------- /app/containers/HomePage/styles.module.css: -------------------------------------------------------------------------------- 1 | .textSection { 2 | margin: 3em auto; 3 | } 4 | 5 | .textSection:first-child { 6 | margin-top: 0; 7 | } 8 | 9 | .centered { 10 | text-align: center; 11 | } 12 | 13 | p, 14 | label { 15 | font-family: Georgia, Times, 'Times New Roman', serif; 16 | line-height: 1.5em; 17 | } 18 | 19 | .link { 20 | text-decoration: none; 21 | } 22 | 23 | .code { 24 | font-size: 0.75em; 25 | background-color: $very-light-grey; 26 | padding: 0.125em 0.5em; 27 | border-radius: 1px; 28 | } 29 | 30 | .usernameForm { 31 | margin-bottom: 1em; 32 | } 33 | 34 | .input { 35 | outline: none; 36 | border-bottom: 1px dotted #999; 37 | } 38 | 39 | .atPrefix { 40 | color: black; 41 | margin-left: 0.4em; 42 | } 43 | -------------------------------------------------------------------------------- /app/tests/store.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Test store addons 3 | */ 4 | 5 | import * as expect from 'expect'; 6 | import configureStore from '../store'; 7 | import { browserHistory } from 'react-router'; 8 | 9 | describe('configureStore', () => { 10 | let store; 11 | 12 | beforeEach(() => { 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "noImplicitAny": false, 7 | "sourceMap": true, 8 | "preserveConstEnums": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "jsx": "preserve", 12 | "lib": ["dom","es5","es6","es7","es2017"], 13 | "allowSyntheticDefaultImports": true, 14 | "typeRoots": ["node_modules/@types"], 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": [ 18 | "*" 19 | ] 20 | } 21 | }, 22 | "exclude": [ 23 | "node_modules" 24 | ], 25 | "awesomeTypescriptLoaderOptions": { 26 | "useBabel": true, 27 | "forkChecker": true, 28 | "useCache": false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /internals/templates/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 | -------------------------------------------------------------------------------- /app/containers/App/styles.module.css: -------------------------------------------------------------------------------- 1 | /* Global styles */ 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | width: 100%; 7 | } 8 | 9 | body { 10 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 11 | } 12 | 13 | body.fontLoaded { 14 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 15 | } 16 | 17 | :global(#app) { 18 | background-color: #FAFAFA; 19 | min-height: 100%; 20 | min-width: 100%; 21 | } 22 | 23 | .wrapper { 24 | max-width: calc(768px + 16px * 2); 25 | margin: 0 auto; 26 | display: flex; 27 | min-height: 100%; 28 | padding: 0 16px; 29 | flex-direction: column; 30 | } 31 | 32 | .logoWrapper { 33 | padding: 2em 0; 34 | } 35 | 36 | .logo { 37 | width: 100%; 38 | margin: 0 auto; 39 | display: block; 40 | } 41 | -------------------------------------------------------------------------------- /internals/templates/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 | 9 | import enLocaleData from 'react-intl/locale-data/en'; 10 | 11 | export const appLocales = [ 12 | 'en', 13 | ]; 14 | 15 | import enTranslationMessages from './translations/en.json'; 16 | 17 | addLocaleData(enLocaleData); 18 | 19 | const formatTranslationMessages = (messages) => { 20 | const formattedMessages = {}; 21 | for (const message of messages) { 22 | formattedMessages[message.id] = message.message || message.defaultMessage; 23 | } 24 | 25 | return formattedMessages; 26 | }; 27 | 28 | export const translationMessages = { 29 | en: formatTranslationMessages(enTranslationMessages), 30 | }; 31 | -------------------------------------------------------------------------------- /internals/templates/homePage/homePage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * HomePage 3 | * 4 | * This is the first thing users see of our App, at the '/' route 5 | * 6 | * NOTE: while this component should technically be a stateless functional 7 | * component (SFC), hot reloading does not currently support SFCs. If hot 8 | * reloading is not a necessity for you then you can refactor it and remove 9 | * the linting exception. 10 | */ 11 | 12 | import React from 'react'; 13 | import { FormattedMessage } from 'react-intl'; 14 | import messages from './messages'; 15 | 16 | export default class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function 17 | 18 | render() { 19 | return ( 20 |

    21 | 22 |

    23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /internals/scripts/pagespeed.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.stdin.resume(); 4 | process.stdin.setEncoding('utf8'); 5 | 6 | var ngrok = require('ngrok'); 7 | var psi = require('psi'); 8 | var 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/containers/HomePage/tests/reducer.test.ts: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | import homeReducer from '../reducer'; 3 | import { 4 | changeUsername, 5 | } from '../actions'; 6 | import { fromJS } from 'immutable'; 7 | 8 | describe('homeReducer', () => { 9 | let state; 10 | beforeEach(() => { 11 | state = fromJS({ 12 | username: '', 13 | }); 14 | }); 15 | 16 | it('should return the initial state', () => { 17 | const expectedResult = state; 18 | expect(homeReducer(undefined, {})).toEqual(expectedResult); 19 | }); 20 | 21 | it('should handle the changeUsername action correctly', () => { 22 | const fixture = 'mxstbr'; 23 | const expectedResult = state.set('username', fixture); 24 | 25 | expect(homeReducer(state, changeUsername(fixture))).toEqual(expectedResult); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /internals/generators/component/es6.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * {{ properCase name }} 4 | * 5 | */ 6 | 7 | import React from 'react'; 8 | 9 | {{#if wantMessages}} 10 | import { FormattedMessage } from 'react-intl'; 11 | import messages from './messages'; 12 | {{/if}} 13 | {{#if wantCSS}} 14 | import styles from './styles.css'; 15 | {{/if}} 16 | 17 | class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function 18 | render() { 19 | return ( 20 | {{#if wantCSS}} 21 |
    22 | {{else}} 23 |
    24 | {{/if}} 25 | {{#if wantMessages}} 26 | 27 | {{/if}} 28 |
    29 | ); 30 | } 31 | } 32 | 33 | export default {{ properCase name }}; 34 | -------------------------------------------------------------------------------- /internals/templates/notFoundPage/notFoundPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NotFoundPage 3 | * 4 | * This is the page we show when the user visits a url that doesn't have a route 5 | * 6 | * NOTE: while this component should technically be a stateless functional 7 | * component (SFC), hot reloading does not currently support SFCs. If hot 8 | * reloading is not a necessity for you then you can refactor it and remove 9 | * the linting exception. 10 | */ 11 | 12 | import React from 'react'; 13 | import { FormattedMessage } from 'react-intl'; 14 | import messages from './messages'; 15 | 16 | export default class NotFound extends React.Component { // eslint-disable-line react/prefer-stateless-function 17 | 18 | render() { 19 | return ( 20 |

    21 | 22 |

    23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/components/List/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | import { mount } from 'enzyme'; 3 | import * as React from 'react'; 4 | 5 | import List from 'app/components/List'; 6 | import ListItem from 'app/components/ListItem'; 7 | 8 | describe('', () => { 9 | it('should render the component if no items are passed', () => { 10 | const renderedComponent = mount( 11 | , 12 | ); 13 | expect(renderedComponent.find(ListItem)).toExist(); 14 | }); 15 | 16 | it('should render the items', () => { 17 | const items = [ 18 | 'Hello', 19 | 'World', 20 | ]; 21 | const renderedComponent = mount( 22 | , 23 | ); 24 | expect(renderedComponent.containsAllMatchingElements(items.map((item) => ))).toExist(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /internals/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | React.js Boilerplate 12 | 13 | 14 | 15 |
    16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/components/ToggleOption/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import ToggleOption from '../index'; 2 | 3 | import * as expect from 'expect'; 4 | import { shallow } from 'enzyme'; 5 | import { IntlProvider, defineMessages } from 'react-intl'; 6 | import * as React from 'react'; 7 | 8 | describe('', () => { 9 | it('should render default language messages', () => { 10 | const defaultEnMessage = 'someContent'; 11 | const message = defineMessages({ 12 | enMessage: { 13 | id: 'app.components.LocaleToggle.en', 14 | defaultMessage: defaultEnMessage, 15 | }, 16 | }); 17 | const renderedComponent = shallow( 18 | 19 | 20 | , 21 | ); 22 | expect(renderedComponent.contains()).toEqual(true); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/components/Footer/tests/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as expect from 'expect'; 2 | import { shallow } from 'enzyme'; 3 | import * as React from 'react'; 4 | 5 | import Footer from 'app/components/Footer'; 6 | import A from 'app/components/A'; 7 | 8 | describe('