├── .babelrc ├── .coveralls.yml ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .nycrc ├── .stylelintrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appveyor.yml ├── package.json ├── preprocessor.js ├── server ├── api │ └── mocks │ │ ├── forecast.json │ │ ├── logo.json │ │ ├── weatherStation.json │ │ └── weatherStations.json ├── enviroment │ ├── development.js │ ├── index.js │ └── production.js ├── httpServer.js ├── index.js ├── lib │ ├── configureStore.js │ ├── files.js │ ├── renderContainer.js │ └── renderPage.js ├── middleware │ ├── dev-middleware.js │ ├── index.js │ ├── prod-middleware.js │ └── routing-middleware.js ├── routing.js ├── server.js ├── statics │ └── index.js └── templates │ ├── calculator.js │ ├── index.js │ └── main.js ├── src ├── app │ ├── assets │ │ └── images │ │ │ ├── favicon.ico │ │ │ ├── github.svg │ │ │ └── react-base-logo.png │ ├── components │ │ ├── LinkButton │ │ │ ├── index.jsx │ │ │ ├── spec │ │ │ │ └── LinkButton.spec.js │ │ │ └── styles.css │ │ └── Logo │ │ │ ├── index.jsx │ │ │ └── styles.css │ ├── containers │ │ ├── App │ │ │ ├── index.jsx │ │ │ └── spec │ │ │ │ └── App.spec.js │ │ ├── Calculator │ │ │ ├── actionTypes │ │ │ │ └── index.js │ │ │ ├── actions │ │ │ │ ├── index.js │ │ │ │ └── spec │ │ │ │ │ └── Calculator.actions.spec.js │ │ │ ├── components │ │ │ │ ├── Button │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── spec │ │ │ │ │ │ └── Button.spec.js │ │ │ │ │ └── styles.css │ │ │ │ ├── ButtonPannel │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── spec │ │ │ │ │ │ └── ButtonPannel.spec.js │ │ │ │ │ └── styles.css │ │ │ │ └── Display │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── spec │ │ │ │ │ └── Display.spec.js │ │ │ │ │ └── styles.css │ │ │ ├── index.jsx │ │ │ ├── models │ │ │ │ └── index.js │ │ │ ├── reducers │ │ │ │ ├── index.js │ │ │ │ └── spec │ │ │ │ │ └── Calculator.reducer.spec.js │ │ │ ├── spec │ │ │ │ └── Calculator.container.spec.js │ │ │ └── styles.css │ │ ├── Main │ │ │ ├── actionTypes │ │ │ │ └── index.js │ │ │ ├── actions │ │ │ │ ├── index.js │ │ │ │ └── spec │ │ │ │ │ └── Logo.action.spec.js │ │ │ ├── api │ │ │ │ └── index.js │ │ │ ├── index.jsx │ │ │ ├── models │ │ │ │ └── index.js │ │ │ ├── reducers │ │ │ │ ├── index.js │ │ │ │ └── spec │ │ │ │ │ └── Logo.reducers.spec.js │ │ │ ├── spec │ │ │ │ └── Main.spec.js │ │ │ └── styles.css │ │ └── WeatherStations │ │ │ ├── actionTypes │ │ │ └── index.js │ │ │ ├── actions │ │ │ ├── index.js │ │ │ └── spec │ │ │ │ └── WeatherStations.action.spec.js │ │ │ ├── api │ │ │ ├── index.js │ │ │ └── spec │ │ │ │ └── weatherStations.api.spec.js │ │ │ ├── components │ │ │ ├── ForecastDetail │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ │ └── ForecastDetail.component.spec.js │ │ │ │ └── styles.css │ │ │ ├── ForecastDetailItem │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ │ └── forecastDetailItem.component.spec.js │ │ │ │ └── styles.css │ │ │ ├── MapBox │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ │ └── MapBox.component.spec.js │ │ │ │ └── styles.css │ │ │ ├── MapInfoWindow │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ │ └── MapInfoWindow.component.spec.js │ │ │ │ └── styles.css │ │ │ ├── MapMarker │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ │ └── MapMarker.component.spec.js │ │ │ │ ├── styles.css │ │ │ │ └── stylesMarker.js │ │ │ └── WeatherStationDetails │ │ │ │ ├── index.jsx │ │ │ │ ├── spec │ │ │ │ └── WeatherStationDetails.component.spec.js │ │ │ │ └── styles.css │ │ │ ├── config.js │ │ │ ├── helpers │ │ │ ├── index.jsx │ │ │ └── spec │ │ │ │ └── WeatherStations.helpers.spec.js │ │ │ ├── index.jsx │ │ │ ├── models │ │ │ ├── ForecastModel.js │ │ │ ├── WeatherStationDetailsModel.js │ │ │ ├── WeatherStationsModel.js │ │ │ └── index.js │ │ │ ├── reducers │ │ │ ├── index.js │ │ │ └── spec │ │ │ │ └── WeatherStations.reducer.spec.js │ │ │ ├── spec │ │ │ └── WeatherStations.container.spec.js │ │ │ └── styles.css │ └── styles │ │ ├── base.css │ │ └── index.js └── base │ ├── client │ └── index.js │ ├── conf │ └── .react-base │ │ ├── logo.js │ │ ├── logo.txt │ │ └── templates │ │ └── generator-react-base │ │ ├── generators │ │ ├── index.js │ │ └── templates │ │ │ ├── _action.js │ │ │ ├── _action.spec.js │ │ │ ├── _actionTypes.js │ │ │ ├── _api.js │ │ │ ├── _component.js │ │ │ ├── _component.spec.js │ │ │ ├── _container.js │ │ │ ├── _container.spec.js │ │ │ ├── _models.js │ │ │ ├── _reducer.js │ │ │ ├── _reducer.spec.js │ │ │ └── _styles.css │ │ └── package.json │ ├── index.js │ ├── models │ └── index.js │ ├── reducers │ └── index.js │ ├── routes │ └── index.js │ ├── shared │ ├── Context.js │ ├── CreateActionType.js │ ├── CreateReducer.js │ ├── Env.js │ ├── Errors.js │ ├── FetchData.js │ ├── FileSystem.js │ ├── GetActionPrefix.js │ ├── ModelHelper.js │ ├── Regenerate.js │ ├── ResolveRequestAction.js │ ├── TestSetup.js │ ├── console.js │ ├── index.js │ ├── regenerators │ │ ├── RegenerateModelIndex.js │ │ ├── RegenerateReducerIndex.js │ │ ├── RegenerateRoutes.js │ │ └── spec │ │ │ ├── RegenerateModelIndex.spec.js │ │ │ └── RegenerateReducerIndex.spec.js │ └── spec │ │ ├── Errors.spec.js │ │ ├── FetchData.shared.spec.js │ │ ├── FileSystem.shared.spec.js │ │ ├── ModelHelper.shared.spec.js │ │ └── TypeHelper.shared.spec.js │ ├── store │ ├── ConfigureStore.js │ ├── GlobalState.js │ ├── InitialState.js │ └── spec │ │ └── ConfigureStore.store.spec.js │ ├── types │ └── FetchTypes.js │ └── wp-plugins │ ├── compileInfoPlugin.js │ ├── fileHashPlugin.js │ └── index.js ├── webpack ├── index.babel.js ├── webpack.common.config.js ├── webpack.dev.config.js ├── webpack.dll.config.babel.js └── webpack.prod.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-0"], 3 | "plugins": ["transform-react-jsx","transform-object-rest-spread","transform-class-properties"], 4 | "env": { 5 | "development": { 6 | "plugins": [ 7 | [ "babel-plugin-webpack-alias", { "config": "./webpack/index.babel.js" } ] 8 | ] 9 | }, 10 | "production": { 11 | "plugins": [ 12 | [ "babel-plugin-webpack-alias", { "config": "./webpack/index.babel.js" } ] 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: 7323SLwgvPvYa3An9lKsN7gD3WYdwtFQA 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | "react" 5 | ], 6 | "extends": ["airbnb-base", "eslint:recommended", "plugin:react/recommended"], 7 | "globals": { 8 | "__DEV__": true, 9 | "describe" : false, 10 | "it" : false 11 | }, 12 | "env": { 13 | "browser": true, 14 | "node": true 15 | }, 16 | "parserOptions": { 17 | "ecmaVersion": 6 18 | }, 19 | "rules": { 20 | "react/require-extension": "off", 21 | "react/jsx-filename-extension": 0, 22 | "react/jsx-pascal-case": 2, 23 | "react/prefer-stateless-function": 0, 24 | "react/prefer-es6-class": 2, 25 | "react/no-multi-comp": 2, 26 | "react/jsx-boolean-value": [2, "always"], 27 | "react/require-render-return": 2, 28 | "react/no-is-mounted": 2, 29 | "react/jsx-wrap-multilines": 1, 30 | "react/jsx-no-bind": 0, 31 | "react/forbid-prop-types": 0, 32 | "jsx-quotes": [2, "prefer-double"], 33 | "react/jsx-curly-spacing": [2, "always"], 34 | "import/default": 0, 35 | "import/no-duplicates": 0, 36 | "import/no-extraneous-dependencies": 0, 37 | "import/extensions": 0, 38 | "import/named": 0, 39 | "import/namespace": 0, 40 | "import/no-unresolved": 0, 41 | "import/no-named-as-default": 0, 42 | "import/prefer-default-export": 0, 43 | "comma-dangle": 0, 44 | "indent": [2, 2, {"SwitchCase": 1}], 45 | "no-console": 0, 46 | "no-alert": 0, 47 | "semi": 2, 48 | "no-var": 2, 49 | "space-before-blocks": 2, 50 | "keyword-spacing": 2, 51 | "no-global-assign": 0, 52 | "no-unsafe-negation": 0, 53 | "space-in-parens": ["error", "never"], 54 | "eol-last": 0, 55 | "no-trailing-spaces": 0, 56 | "padded-blocks": 0, 57 | "arrow-parens": 0, 58 | "linebreak-style": 0, 59 | "no-use-before-define": 0 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .idea 3 | coverage/ 4 | dist/ 5 | build/ 6 | *.vimrc 7 | .DS_STORE 8 | node_modules/ 9 | bower_components/ 10 | .nyc_output/ 11 | .vscode/ 12 | package-lock.json 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | spec 3 | coverage 4 | examples 5 | .babelrc 6 | .eslintrc.js 7 | .nyc_output 8 | .travis.yml 9 | .appveyor.yml 10 | .nycrc 11 | mocha.opts 12 | tsconfig.json 13 | tslint.json 14 | *.swp 15 | *.log 16 | .coveralls.yml 17 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [ 3 | "webpack/**", 4 | "**/*.spec.js", 5 | "src/base/wp-plugins/**" 6 | ], 7 | "extension": [ 8 | ".jsx", 9 | ".es6", 10 | "js" 11 | ] 12 | } -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "block-no-empty": null, 4 | "color-no-invalid-hex": true, 5 | "declaration-colon-space-after": "always", 6 | "indentation": [2, { 7 | "except": ["value"] 8 | }], 9 | "max-empty-lines": 2, 10 | "unit-whitelist": ["em", "rem", "%", "s", "px"] 11 | } 12 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.2.0" 4 | before_script: 5 | - npm install 6 | env: 7 | - NODE_ENV=development 8 | script: 9 | - npm run build:prod 10 | - npm run generate:dll 11 | - npm run regenerate 12 | - npm run test:coverage 13 | - npm run coveralls 14 | - npm run lint:all 15 | notifications: 16 | slack: 17 | on_pull_requests: false 18 | rooms: 19 | - cejs-atsistemas:XxetFLj7MuJALpcVhx2PKEE6#react-base 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.0.1 / 2017-06-05 2 | ================== 3 | 4 | * Add Webpack2 support 5 | * Add Changelog.md 6 | * Updated to React 15.5.4 7 | * Updated React Test Addons Utils 8 | * Updated webpack assets file 9 | * Updated contributors in package.json -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to React-Base 2 | 3 | Love [React-Base](https://html5boilerplate.com) and want to get involved? 4 | Awesome! There are plenty of ways you can help! 5 | 6 | Please take a moment to review this document in order to make the contribution 7 | process easy and effective for everyone involved. 8 | 9 | Following these guidelines helps to communicate that you respect the time of 10 | the developers managing and developing this open source project. In return, 11 | they should reciprocate that respect in addressing your issue or assessing 12 | patches and features. 13 | 14 | 15 | ## Using the issue tracker 16 | 17 | The [issue tracker](https://github.com/atsistemas/react-base/issues) is 18 | the preferred channel for [bug reports](#bugs), [features requests](#features) 19 | and [submitting pull requests](#pull-requests), but please respect the following restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. 22 | 23 | * Please **do not** derail or troll issues. Keep the discussion on topic and 24 | respect the opinions of others. 25 | 26 | * Please **do not** open issues or pull requests regarding the code in React-Base third party modules. 27 | 28 | 29 | 30 | ## Bug reports 31 | 32 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 33 | Good bug reports are extremely helpful - thank you! 34 | 35 | Guidelines for bug reports: 36 | 37 | 1. **Use the GitHub issue search** — check if the issue has already been 38 | reported. 39 | 40 | 2. **Check if the issue has been fixed** — try to reproduce it using the 41 | latest `master` branch in the repository. 42 | 43 | 3. **Isolate the problem** — ideally create a [reduced test 44 | case](https://css-tricks.com/reduced-test-cases/) and a live example. 45 | 46 | A good bug report shouldn't leave others needing to chase you up for more 47 | information. Please try to be as detailed as possible in your report. What is 48 | your environment? What steps will reproduce the issue? What browser(s) and OS 49 | experience the problem? What would you expect to be the outcome? All these 50 | details will help people to fix any potential bugs. 51 | 52 | Example: 53 | 54 | > Short and descriptive example bug report title 55 | > 56 | > A summary of the issue and the browser/OS environment in which it occurs. If 57 | > suitable, include the steps required to reproduce the bug. 58 | > 59 | > 1. This is the first step 60 | > 2. This is the second step 61 | > 3. Further steps, etc. 62 | > 63 | > `` - a link to the reduced test case 64 | > 65 | > Any other information you want to share that is relevant to the issue being 66 | > reported. This might include the lines of code that you have identified as 67 | > causing the bug, and potential solutions (and your opinions on their 68 | > merits). 69 | 70 | 71 | 72 | ## Feature requests 73 | 74 | Feature requests are welcome. But take a moment to find out whether your idea 75 | fits with the scope and aims of the project. It's up to *you* to make a strong 76 | case to convince the project's developers of the merits of this feature. Please 77 | provide as much detail and context as possible. 78 | 79 | 80 | 81 | ## Pull requests 82 | 83 | Good pull requests - patches, improvements, new features - are a fantastic 84 | help. They should remain focused in scope and avoid containing unrelated 85 | commits. 86 | 87 | **Please ask first** before embarking on any significant pull request (e.g. 88 | implementing features, refactoring code, porting to a different language), 89 | otherwise you risk spending a lot of time working on something that the 90 | project's developers might not want to merge into the project. 91 | 92 | Please adhere to the coding conventions used throughout a project (indentation, 93 | accurate comments, etc.) and any other requirements (such as test coverage). 94 | 95 | Adhering to the following process is the best way to get your work 96 | included in the project: 97 | 98 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your 99 | fork, and configure the remotes: 100 | 101 | ```bash 102 | # Clone your fork of the repo into the current directory 103 | git clone https://github.com//react-base.git 104 | # Navigate to the newly cloned directory 105 | cd react-base 106 | # Assign the original repo to a remote called "upstream" 107 | git remote add upstream https://github.com/atsistemas/react-base.git 108 | ``` 109 | 110 | 2. If you cloned a while ago, get the latest changes from upstream: 111 | 112 | ```bash 113 | git checkout master 114 | git pull upstream master 115 | ``` 116 | 117 | 3. Create a new topic branch (off the main project development branch) to 118 | contain your feature, change, or fix: 119 | 120 | ```bash 121 | git checkout -b [feat|fix]/ 122 | ``` 123 | Using *feat* for new features, or *fix* for bug fixing. 124 | 125 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 126 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 127 | or your code is unlikely be merged into the main project. Use Git's 128 | [interactive rebase](https://help.github.com/articles/about-git-rebase/) 129 | feature to tidy up your commits before making them public. 130 | 131 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 132 | 133 | ```bash 134 | git pull [--rebase] upstream master 135 | ``` 136 | 137 | 6. Push your topic branch up to your fork: 138 | 139 | ```bash 140 | git push origin [feat|fix]/ 141 | ``` 142 | 143 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 144 | with a clear title and description. 145 | 146 | **IMPORTANT**: By submitting a patch, you agree to allow the project 147 | owners to license your work under the terms of the [MIT License](LICENSE). 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ce Js @ atSistemas 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![React-Base logo](./src/app/assets/images/react-base-logo.png) 2 | 3 | # React-Base 4 | 5 | 6 | ![Build-Status](https://travis-ci.org/atSistemas/react-base.svg?branch=master) 7 | ![Build-Status](https://ci.appveyor.com/api/projects/status/github/atSistemas/react-base?branch=master&svg=true) 8 | [![Coverage Status](https://coveralls.io/repos/github/atSistemas/react-base/badge.svg?branch=master)](https://coveralls.io/github/atSistemas/react-base?branch=master) 9 | ![Npm-Version](https://img.shields.io/badge/npm-6.2.0-blue.svg) 10 | ![License](https://img.shields.io/badge/license-MIT-blue.svg) 11 | 12 | **A modular platform for Redux Isomorphic applications** 13 | 14 | This repository is a modular abstraction to build a [ReactJS](https://facebook.github.io/react/) web application based on [Redux](http://redux.js.org/) paradigm. 15 | You can use it to quickly scaffold your React web application projects and development environments for these projects. 16 | 17 | This seed should clarify how to wire up all the modules of your application, even when we understand that in some cases 18 | there must be some changes needed by the structure to fit your needs correctly 19 | 20 | ## Overview 21 | 22 | **React-Base** makes use of the latest tools to improve your workflow, and enables you to create future ready applications: 23 | 24 | - [Redux](http://redux.js.org/) based architecture 25 | - Isomorphic / Universal Javascript Apps 26 | - [Webpack 3](https://webpack.github.io/) build configuration depending on enviroment 27 | - Immutable data modeling using [ImmutableJS](https://facebook.github.io/immutable-js/) 28 | - Store middleware to handle request actions. 29 | - Development & Production server using [express](https://github.com/expressjs/express) and [webpack-dev-server](https://webpack.github.io/) 30 | - Hot Reload/Live Reload support for Js & Css using [Webpack HMR](https://webpack.github.io/docs/hot-module-replacement.html) 31 | - Container and component generators using [Yeoman](https://github.com/yeoman/yo) 32 | - JSX and ES6 transpilation using [Babel](https://babeljs.io/) 33 | - [Mocha](https://mochajs.org/) as testing framework 34 | - [Enzyme/JsDom](https://github.com/airbnb/enzyme) for unit/ui testing 35 | - [Nyc](https://github.com/bcoe/nyc) for code coverage 36 | - [PostCSS](http://postcss.org/) processing with isomorphic support. 37 | - [CssModules](https://github.com/css-modules/css-modules) based 38 | - Code Linting using [Eslint](https://github.com/eslint/eslint) 39 | - Css Linting using [CssLint](https://github.com/stylelint/stylelint) 40 | - [Airbnb](https://github.com/airbnb/javascript/tree/master/react) React Style Guide 41 | 42 | 43 | ## Getting Started 44 | 45 | To get you started, you need to meet the prerequisites, and then follow the installation instructions. 46 | 47 | ### Prerequisites 48 | 49 | React-Base makes use a number of NodeJS tools to initialize and test React-Base. You must have node.js 6.2.0 at least, and its package manager (npm) installed. You can get it from [nodejs.org](node). 50 | 51 | ### Installing 52 | 53 | You can clone our Git repository: 54 | 55 | `$ git clone https://github.com/atSistemas/react-base.git` 56 | 57 | This method requires Git to be installed on your computer. You can get it from 58 | [here](http://git-scm.com). 59 | 60 | ### Wiring up your development environment 61 | 62 | Setting up **React-Base** is as easy as running: 63 | 64 | `$ npm install` 65 | 66 | This command will install all the required dependencies and start your development server, which takes care of all the changes you make to your code and runs all the awesome stuff that ends with your code automagically transpiled and running on your browser. 67 | 68 | Please note that `npm install` is only required on your first start, or in case of updated dependencies. 69 | 70 | 71 | ### Initializing development server 72 | 73 | Once all the dependencies are installed, you can run `$ npm run start` to initialize your development server using [webpack-dev-server](https://webpack.github.io/) express middleware. 74 | 75 | The dev server uses [HMR](https://webpack.github.io/docs/hot-module-replacement.html) (Hot module replacement) that injects updated modules into the bundle in runtime. It's like LiveReload 76 | 77 | 78 | ## Architecture 79 | 80 | React-base is based on [Redux](http://redux.js.org/) paradigm so you can find all the typical entities of an Redux project like [reducers](http://redux.js.org/docs/basics/Reducers.html) , [store](http://redux.js.org/docs/basics/Store.html), [actions](http://redux.js.org/docs/basics/Actions.html) , etc. 81 | 82 | There are four main folders: 83 | 84 | * `server` contains React-Base development & production server based in express with Universal/Isomorphic support and custom middlewares like Gzip. 85 | 86 | ```javascript 87 | server 88 | api/ //Api mocks 89 | lib/ //Universal rendering files 90 | middleware/ //enviroment middleware 91 | statics/ //definition of statics path 92 | templates/ //universal templates 93 | server //Server 94 | routing //Routing middleware 95 | ``` 96 | 97 | * `webpack` contains React-Base Webpack configuration separated by enviroment that allows to use different plugins and loaders in each target enviroment. 98 | 99 | ```javascript 100 | webpack 101 | webpack.common.config/ //Common config 102 | webpack.dev.config/ //Development config 103 | webpack.prod.config/ //Production config 104 | webpack.test.config/ //Testing config 105 | webpack.dll.config/ //Dll config 106 | ``` 107 | 108 | * `src/base/` contains React-Base platform bootstrapping code. 109 | 110 | ```javascript 111 | base 112 | client/ //client bootstrap 113 | conf/ //Configuration files and Yeoman templates 114 | middleware/ //Redux Store middleware 115 | components/ //base components 116 | models/ //model index 117 | reducers/ //reducer index 118 | routes/ //routes index 119 | shared/ // shared base folder 120 | regenerators/ //index regenerators 121 | CreateActionType //Custom action type creator 122 | CreateReducer //Custom reducer creator 123 | ENV //Env handler 124 | Errors //Errors handler 125 | FetchData //Isomorfic data handler 126 | FileSystem //Filesystem manager 127 | JsDomSetup //JsDom Configuration FileSystem 128 | ModelHelper //Inmutable deserializators 129 | Regenerate // Regenerate indexes 130 | store/ //Store configuration and AppState definition 131 | types/ //Action request Types 132 | wp-plugins/ //Custom webpack plugins 133 | ... 134 | ``` 135 | 136 | * `src/app/` is the place where to put your application source code. 137 | 138 | React-Base uses a "featured based" distribution, so all the necessary code for each page/features is located in its own folder inside containers folder as in `src/app/containers/myContainer` 139 | 140 | A container is a React component who contains other components, Redux entities, functions and store subscriptions. Each container is self-contained and represents a feature like "clients" or "products" and it contains all the necessary stuff. 141 | ```javascript 142 | app/ 143 | containers/ 144 | myContainer/ 145 | api/ //api calls 146 | actionTypes/ //action types definition 147 | actions/ //action creators 148 | components/ //container components 149 | models/ //containers models using immutable 150 | reducers/ //container reducers 151 | index.ts //container component 152 | ... 153 | ``` 154 | 155 | ## Action Types 156 | ActionTypes it's a representation using constants of your possible actions: 157 | 158 | ```javascript 159 | import { createActionType } from 'base'; 160 | 161 | export const ActionTypes = createActionType([ 162 | 'CLICK', 163 | 'MAIN_CONTAINER', 164 | 'MAIN_ERROR', 165 | 'MAIN_REQUEST', 166 | 'MAIN_SUCCESS', 167 | 'LAZY_CONTAINER', 168 | 'LOGIN', 169 | ]); 170 | 171 | ``` 172 | 173 | ## Actions 174 | Actions are payloads of information witch represent that something happend in your application and send data from your application to your store: 175 | 176 | ```javascript 177 | clickHandler(id) { 178 | return { 179 | type: ActionTypes.USER_CLICK, 180 | payload: { 181 | id: id 182 | } 183 | }; 184 | } 185 | 186 | ``` 187 | 188 | React-Base include a Redux Store middleware to handle actions with service calls more easyly. You can define in the api folder of your container, an api call based in a fetch call: 189 | 190 | ```javascript 191 | 192 | fetchUsers() { 193 | return fetch(url) 194 | .then(req => req.json()) 195 | .then(data => data) 196 | .catch(err => err) 197 | }, 198 | 199 | ``` 200 | 201 | Then, in your action you can attach this service call in your action using the request param: 202 | 203 | ```javascript 204 | export function getPosts() { 205 | return { 206 | type: ActionTypes.USERS_REQUEST, 207 | request: api.fetchUsers() 208 | }; 209 | } 210 | ``` 211 | 212 | The request middleware will resolve the request param 213 | and dispatch a new action with "ACTION_SUCCESS" or "ACTION_ERROR" with the response of the request in the payload. 214 | 215 | ## Reducers 216 | Reducers describe how the state of your application changes in response to a new Action. React-Base uses a custom CreateReducer that allows to use separated reducers functions instead of "switch based" reducers. 217 | 218 | ```javascript 219 | import { createReducer } from 'base'; 220 | 221 | const click = (state, action) => { 222 | return state.update('mainData', (value) => action.payload); 223 | }; 224 | 225 | const request = (state, action) => { 226 | return state; 227 | }; 228 | 229 | const actionHandlers = { 230 | [ActionTypes.CLICK]: click, 231 | [ActionTypes.LOGIN]: login, 232 | [ActionTypes.MAIN_REQUEST]: request, 233 | [ActionTypes.MAIN_SUCCESS]: success, 234 | }; 235 | 236 | export default CreateReducer(actionHandlers, new MainModel()); 237 | 238 | ``` 239 | 240 | ## Models 241 | Represents your model data using ImmutableJS Data Types and sets its initial state using setInitialState() function. 242 | 243 | 244 | ```javascript 245 | import { Record } from 'immutable'; 246 | 247 | const MainModel = new Record({ 248 | display:0, 249 | operator:'', 250 | operation:'', 251 | prevValue: 0, 252 | nextValue: 0, 253 | newValue: false, 254 | resetDisplay: false, 255 | }); 256 | 257 | function setInitialState(initialState) { 258 | return initialState.Maiin = new MainModel(); 259 | } 260 | 261 | export { MainModel, setInitialState }; 262 | 263 | 264 | ``` 265 | ### Generating a new container 266 | 267 | React-base uses Yeoman to generate new application containers or components. 268 | 269 | Fist of all you need to link yo: 270 | 271 | `$ npm run yo` 272 | 273 | Then, you can generate a new container run: 274 | 275 | `$ npm run generate:container` 276 | 277 | You'll be asked to provide a name for your container. After that, React-base will create all the necessary folder and file template structures you, and will rebuild the file indexes (routes, reducers, models, etc), so you don't have to worry about including all the required imports. 278 | 279 | After that, you can access to your container from http://localhost:8000/myContainer 280 | 281 | ### Regenerating indexes 282 | 283 | You can rebuild the file indexes (reducers, models and routes) running `$ npm run regenerate` 284 | 285 | ### Generating a new component 286 | 287 | As with containers, React-base can automate components creation for you. To create a new component, just type: 288 | 289 | `$ npm run generate:component` 290 | 291 | Same as before, you will be asked for a component name, and after that React-base will do the rest, placing a component template under `app/components`, and rebuilding all the indexes. 292 | 293 | ## Distribution 294 | 295 | You can generate a complete distribution source ready for production enviroments. 296 | 297 | ### Building your production application 298 | 299 | `$ npm run build:prod` will create a minified version for your application, ready for production. 300 | 301 | ### Running production server 302 | 303 | `$ npm run start:prod` will run production enviroment of your application serving content from dist directory. 304 | 305 | 306 | ## Testing your application 307 | 308 | React base uses - [Enzyme](https://github.com/airbnb/enzyme) a testing utillity created by [Airbnb](https://github.com/airbnb/) for unit testing and Ui testing using [Airbnb](https://github.com/tmpvar/jsdom) so you can run your ui testing without a browser. 309 | 310 | You can write your tests normally using Mocha and Chai for assertions. 311 | 312 | ### Running your tests 313 | 314 | `$ npm run test` will perform your unit testing, or npm test:coverage to run your tests and display a code coverage report. 315 | 316 | ### Generating code coverage 317 | 318 | React base uses [Nyc](https://github.com/bcoe/nyc) for code coverage and you can generate reports in console or icov/html format. 319 | 320 | `$ npm run test` will perform your code coverage, generating an html report located in coverage/ folder. 321 | 322 | ## Contributing 323 | 324 | Anyone and everyone is welcome to contribute, however, if you decide to get involved, please take a moment to review the [guidelines](CONTRIBUTING.md): 325 | 326 | * [Bug reports](CONTRIBUTING.md#bugs) 327 | * [Feature requests](CONTRIBUTING.md#features) 328 | * [Pull requests](CONTRIBUTING.md#pull-requests) 329 | 330 | ## License 331 | 332 | **React-Base** is available under the [MIT license](LICENSE). 333 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | 2 | environment: 3 | matrix: 4 | - nodejs_version: 8.9.1 5 | 6 | version: "{build}" 7 | build: off 8 | deploy: off 9 | matrix: 10 | fast_finish: true 11 | 12 | install: 13 | - ps: Install-Product node $env:nodejs_version 14 | - npm install 15 | 16 | test_script: 17 | - npm run build:prod 18 | - npm run generate:dll 19 | - npm run test:coverage 20 | - npm run lint:all 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-base-starter", 3 | "version": "1.1.2", 4 | "description": "React Base React/Redux Isomorphic Starter", 5 | "author": "Pablo Magaz", 6 | "contributors": [ 7 | { 8 | "name": "Luis Martinez Marina", 9 | "email": "lmartinez.marina@atsistemas.com" 10 | }, 11 | { 12 | "name": "Santiago Fernandez Blanco", 13 | "email": "sfernandez.blanco@atsistemas.com" 14 | }, 15 | { 16 | "name": "Gastón Messeri", 17 | "email": "gemesseri@atsistemas.com" 18 | }, 19 | { 20 | "name": "Fernando Ramos González", 21 | "email": "framos@atsistemas.com" 22 | } 23 | ], 24 | "main": "client/client.js", 25 | "scripts": { 26 | "logo": "node ./src/base/conf/.react-base/logo.js", 27 | "webpack": "node_modules/webpack/bin/webpack.js", 28 | "clean": "rimraf dist/ && npm run generate:dll", 29 | "clean:files": "rimraf dist/app.js", 30 | "generate": "npm run logo && yo react-base", 31 | "generate:component": "npm run logo && yo react-base --option=2", 32 | "generate:container": "npm run logo && yo react-base --option=1", 33 | "regenerate:modelIndex": "node ./src/base/shared/Regenerate.js --model", 34 | "regenerate:routes": "node ./src/base/shared/Regenerate.js --routes", 35 | "regenerate:reducerIndex": "node ./src/base/shared/Regenerate.js --reducer", 36 | "regenerate": "npm run logo && node ./src/base/shared/Regenerate.js --all", 37 | "generate:dll": "cross-env NODE_ENV=dll webpack --config webpack/webpack.dll.config.babel.js", 38 | "build": "cross-env NODE_ENV=development webpack --config webpack/index.babel.js", 39 | "build:prod": "npm run clean && cross-env NODE_ENV=production webpack --config webpack/index.babel.js", 40 | "lint": "npm run logo && node node_modules/eslint/bin/eslint.js src --ignore-pattern **/*.spec.js --ext .js --ext .jsx", 41 | "lint:css": "npm run logo && stylelint \"src/**/*.css\" --config .stylelintrc", 42 | "lint:all": "npm run lint && npm run lint:css", 43 | "test": "npm run logo && mocha --require babel-core/register --recursive \"./src/**/*.spec.js\" --require ignore-styles \"./src/base/shared/TestSetup.js\"", 44 | "test:coverage": "nyc npm test && nyc report --reporter=lcov", 45 | "start": "npm run logo && cross-env NODE_ENV=development node server/", 46 | "start:prod": "npm run build:prod && cross-env NODE_ENV=production node server/", 47 | "yo": "npm link src/base/conf/.react-base/templates/generator-react-base", 48 | "postYeomanGenerator": "npm run regenerate && npm run start", 49 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", 50 | "postinstall": "npm run generate:dll", 51 | "prepublishOnly": "npm run lint && npm run test:coverage && npm run build" 52 | 53 | }, 54 | "dependencies": { 55 | "babel-core": "6.26.0", 56 | "compression": "1.7.1", 57 | "express": "4.16.2", 58 | "google-map-react": "0.25.0", 59 | "helmet": "3.9.0", 60 | "react": "16.1.1", 61 | "react-dom": "16.1.1", 62 | "react-redux": "5.0.6", 63 | "react-router": "3.2.0", 64 | "recompose": "0.26.0", 65 | "redux": "3.7.2", 66 | "redux-req-middleware": "1.0.4" 67 | }, 68 | "devDependencies": { 69 | "assets-webpack-plugin": "3.5.1", 70 | "babel-cli": "6.26.0", 71 | "babel-eslint": "8.0.2", 72 | "babel-loader": "7.1.2", 73 | "babel-plugin-transform-class-properties": "6.24.1", 74 | "babel-plugin-transform-object-rest-spread": "6.26.0", 75 | "babel-plugin-webpack-alias": "2.1.2", 76 | "babel-preset-es2015": "6.24.1", 77 | "babel-preset-react": "6.24.1", 78 | "babel-preset-react-hmre": "1.1.1", 79 | "babel-preset-stage-0": "6.24.1", 80 | "babel-register": "6.26.0", 81 | "chai": "4.1.2", 82 | "classnames": "2.2.5", 83 | "copy-webpack-plugin": "4.2.1", 84 | "coveralls": "3.0.0", 85 | "create-reduxreducer": "1.0.0", 86 | "cross-env": "5.1.1", 87 | "css-loader": "0.28.7", 88 | "css-modules-require-hook": "4.2.2", 89 | "enzyme": "3.2.0", 90 | "enzyme-adapter-react-16": "1.1.0", 91 | "eslint": "4.11.0", 92 | "eslint-config-airbnb-base": "12.1.0", 93 | "eslint-loader": "1.9.0", 94 | "eslint-plugin-import": "2.8.0", 95 | "eslint-plugin-jsx-a11y": "6.0.2", 96 | "eslint-plugin-react": "7.5.1", 97 | "extract-text-webpack-plugin": "3.0.2", 98 | "generator-webapp": "3.0.1", 99 | "ignore-styles": "5.0.1", 100 | "immutable": "3.8.2", 101 | "isomorphic-fetch": "2.2.1", 102 | "istanbul": "0.4.5", 103 | "jsdom": "11.4.0", 104 | "mocha": "4.0.1", 105 | "nyc": "11.3.0", 106 | "postcss-browser-reporter": "0.5.0", 107 | "postcss-clean": "1.1.0", 108 | "postcss-import": "11.0.0", 109 | "postcss-loader": "2.0.8", 110 | "postcss-mixins": "6.2.0", 111 | "postcss-modules-extract-imports": "1.1.0", 112 | "postcss-nested": "2.1.2", 113 | "postcss-reporter": "5.0.0", 114 | "postcss-url": "7.2.1", 115 | "precss": "2.0.0", 116 | "progress-bar-webpack-plugin": "1.10.0", 117 | "react-addons-linked-state-mixin": "15.6.2", 118 | "react-controllables": "0.6.0", 119 | "react-ink": "6.2.0", 120 | "react-pure-render": "1.0.2", 121 | "react-test-renderer": "16.1.1", 122 | "redux-logger": "3.0.6", 123 | "rimraf": "2.6.2", 124 | "sinon": "4.1.2", 125 | "style-loader": "0.19.0", 126 | "stylelint": "8.2.0", 127 | "supports-color": "5.0.0", 128 | "webpack": "3.8.1", 129 | "webpack-dev-middleware": "1.12.1", 130 | "webpack-dev-server": "2.9.4", 131 | "webpack-hot-middleware": "2.20.0", 132 | "yeoman-option-or-prompt": "1.0.2", 133 | "yo": "1.8.5" 134 | }, 135 | "repository": { 136 | "type": "git", 137 | "url": "https://github.com/atsistemas/react-base" 138 | }, 139 | "bugs": { 140 | "url": "https://github.com/atsistemas/react-base/issues" 141 | }, 142 | "license": "MIT", 143 | "keywords": [ 144 | "react", 145 | "redux", 146 | "starter", 147 | "starterKit", 148 | "seed", 149 | "isomorphic", 150 | "universal", 151 | "postcss", 152 | "css-modules", 153 | "webpack", 154 | "enzyme", 155 | "testing", 156 | "yeoman", 157 | "istambul", 158 | "immutablejs", 159 | "mocha", 160 | "chai", 161 | "postcss" 162 | ] 163 | } 164 | -------------------------------------------------------------------------------- /preprocessor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const babel = require('babel-core'); 4 | const jestPreset = require('babel-preset-jest'); 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | if (babel.util.canCompile(filename)) { 9 | return babel.transform(src, { 10 | filename, 11 | presets: [jestPreset], 12 | retainLines: true, 13 | }).code; 14 | } 15 | return src; 16 | }, 17 | }; -------------------------------------------------------------------------------- /server/api/mocks/forecast.json: -------------------------------------------------------------------------------- 1 | { 2 | "city": { 3 | "coord": { 4 | "lat": 38.98333, 5 | "lon": -3.93333 6 | }, 7 | "country": "ES", 8 | "id": 2519402, 9 | "name": "Ciudad Real", 10 | "population": 0 11 | }, 12 | "cnt": 8, 13 | "cod": "200", 14 | "list": [ 15 | { 16 | "id": 1, 17 | "clouds": 0, 18 | "deg": 204, 19 | "dt": 1469793600, 20 | "humidity": 24, 21 | "pressure": 951.51, 22 | "speed": 2.36, 23 | "temp": { 24 | "day": 43.94, 25 | "eve": 37.62, 26 | "max": 43.94, 27 | "min": 23.04, 28 | "morn": 34.44, 29 | "night": 23.04 30 | }, 31 | "weather": [ 32 | { 33 | "description": "cielo claro", 34 | "icon": "01d", 35 | "id": 800, 36 | "main": "Clear" 37 | } 38 | ] 39 | }, 40 | { 41 | "id": 2, 42 | "clouds": 0, 43 | "deg": 226, 44 | "dt": 1469880000, 45 | "humidity": 23, 46 | "pressure": 952.13, 47 | "rain": 0.9, 48 | "speed": 5.86, 49 | "temp": { 50 | "day": 36.32, 51 | "eve": 32.56, 52 | "max": 37.29, 53 | "min": 20.61, 54 | "morn": 20.61, 55 | "night": 22.14 56 | }, 57 | "weather": [ 58 | { 59 | "description": "lluvia ligera", 60 | "icon": "10d", 61 | "id": 500, 62 | "main": "Rain" 63 | } 64 | ] 65 | }, 66 | { 67 | "id": 3, 68 | "clouds": 92, 69 | "deg": 215, 70 | "dt": 1469966400, 71 | "humidity": 26, 72 | "pressure": 952.84, 73 | "rain": 0.31, 74 | "speed": 6.37, 75 | "temp": { 76 | "day": 33.29, 77 | "eve": 30.28, 78 | "max": 33.29, 79 | "min": 17.2, 80 | "morn": 20.85, 81 | "night": 17.2 82 | }, 83 | "weather": [ 84 | { 85 | "description": "lluvia ligera", 86 | "icon": "10d", 87 | "id": 500, 88 | "main": "Rain" 89 | } 90 | ] 91 | }, 92 | { 93 | "id": 4, 94 | "clouds": 0, 95 | "deg": 243, 96 | "dt": 1470052800, 97 | "humidity": 0, 98 | "pressure": 949.3, 99 | "speed": 3.26, 100 | "temp": { 101 | "day": 32.92, 102 | "eve": 32.75, 103 | "max": 32.92, 104 | "min": 15.45, 105 | "morn": 15.45, 106 | "night": 20.82 107 | }, 108 | "weather": [ 109 | { 110 | "description": "cielo claro", 111 | "icon": "01d", 112 | "id": 800, 113 | "main": "Clear" 114 | } 115 | ] 116 | }, 117 | { 118 | "id": 5, 119 | "clouds": 54, 120 | "deg": 254, 121 | "dt": 1470139200, 122 | "humidity": 0, 123 | "pressure": 947.83, 124 | "speed": 4.62, 125 | "temp": { 126 | "day": 30.16, 127 | "eve": 30.96, 128 | "max": 30.96, 129 | "min": 16.63, 130 | "morn": 16.63, 131 | "night": 21.67 132 | }, 133 | "weather": [ 134 | { 135 | "description": "lluvia ligera", 136 | "icon": "10d", 137 | "id": 500, 138 | "main": "Rain" 139 | } 140 | ] 141 | }, 142 | { 143 | "id": 6, 144 | "clouds": 0, 145 | "deg": 54, 146 | "dt": 1470225600, 147 | "humidity": 0, 148 | "pressure": 947.95, 149 | "speed": 2.3, 150 | "temp": { 151 | "day": 29.76, 152 | "eve": 30.55, 153 | "max": 30.55, 154 | "min": 17.54, 155 | "morn": 17.57, 156 | "night": 17.54 157 | }, 158 | "weather": [ 159 | { 160 | "description": "cielo claro", 161 | "icon": "01d", 162 | "id": 800, 163 | "main": "Clear" 164 | } 165 | ] 166 | }, 167 | { 168 | "id": 7, 169 | "clouds": 0, 170 | "deg": 52, 171 | "dt": 1470312000, 172 | "humidity": 0, 173 | "pressure": 946.94, 174 | "speed": 0.28, 175 | "temp": { 176 | "day": 30.53, 177 | "eve": 32.43, 178 | "max": 32.43, 179 | "min": 13.42, 180 | "morn": 13.42, 181 | "night": 18.48 182 | }, 183 | "weather": [ 184 | { 185 | "description": "cielo claro", 186 | "icon": "01d", 187 | "id": 800, 188 | "main": "Clear" 189 | } 190 | ] 191 | }, 192 | { 193 | "id": 8, 194 | "clouds": 9, 195 | "deg": 223, 196 | "dt": 1470398400, 197 | "humidity": 0, 198 | "pressure": 948.14, 199 | "speed": 1.39, 200 | "temp": { 201 | "day": 33.86, 202 | "eve": 34.73, 203 | "max": 34.73, 204 | "min": 16.1, 205 | "morn": 16.1, 206 | "night": 22.82 207 | }, 208 | "weather": [ 209 | { 210 | "description": "cielo claro", 211 | "icon": "01d", 212 | "id": 800, 213 | "main": "Clear" 214 | } 215 | ] 216 | } 217 | ], 218 | "message": 0.0517 219 | } -------------------------------------------------------------------------------- /server/api/mocks/logo.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "alt": "React Base!", 4 | "name": "ReactBaseLogo", 5 | "width": 500, 6 | "src": "/assets/images/react-base-logo.png" 7 | } 8 | -------------------------------------------------------------------------------- /server/api/mocks/weatherStation.json: -------------------------------------------------------------------------------- 1 | { 2 | "last": { 3 | "dt": 1452416940, 4 | "main": { 5 | "humidity": 79, 6 | "pressure": 1000, 7 | "temp": 284.82 8 | }, 9 | "rain": { 10 | "today": 0 11 | }, 12 | "wind": { 13 | "deg": 14, 14 | "speed": 14 15 | }, 16 | "visibility": { 17 | 18 | } 19 | }, 20 | "params": [ 21 | "temp", 22 | "pressure", 23 | "humidity", 24 | "wind", 25 | "rain", 26 | "visibility" 27 | ], 28 | "station": { 29 | "coord": { 30 | "lat": 42.8668, 31 | "lon": -2.6793 32 | }, 33 | "id": 120139, 34 | "name": "EA2CQ-13", 35 | "status": 20, 36 | "type": 2 37 | } 38 | } -------------------------------------------------------------------------------- /server/api/mocks/weatherStations.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "stationId": 1442829648, 5 | "distance": 9.168, 6 | "calc": { 7 | "dewpoint": 11.1833 8 | }, 9 | "dt": 1470034503, 10 | "main": { 11 | "humidity": 37.5, 12 | "pressure": 1019.49, 13 | "temp": 300.05 14 | }, 15 | "coord": { 16 | "Lat": 40.495, 17 | "Lon": -3.655 18 | }, 19 | "station": { 20 | "id": 1442829648, 21 | "name": "pirates", 22 | "status": 20, 23 | "type": 5, 24 | "user_id": 0 25 | } 26 | }, 27 | { 28 | "id": 2, 29 | "stationId": 42622, 30 | "distance": 9.839, 31 | "dt": 1469991990, 32 | "main": { 33 | "humidity": 37.5, 34 | "pressure": 1019.49, 35 | "temp": 300.05 36 | }, 37 | "rain": { 38 | "1h": 0, 39 | "24h": 0, 40 | "today": 0 41 | }, 42 | "wind": { 43 | "deg": 11, 44 | "gust": 1.54, 45 | "speed": 0.51 46 | }, 47 | "coord": { 48 | "lat": 40.5048, 49 | "lng": -3.6668 50 | }, 51 | "station": { 52 | "id": 42622, 53 | "name": "EA4ENR", 54 | "status": 20, 55 | "type": 2 56 | } 57 | }, 58 | { 59 | "id": 3, 60 | "stationId": 5488, 61 | "distance": 13.928, 62 | "dt": 1470033000, 63 | "main": { 64 | "humidity": 29, 65 | "pressure": 1014, 66 | "temp": 307.59 67 | }, 68 | "visibility": { 69 | "distance": 10000, 70 | "prefix": 1 71 | }, 72 | "wind": { 73 | "deg": 10, 74 | "speed": 3.1, 75 | "var_beg": 340, 76 | "var_end": 40 77 | }, 78 | "coord": { 79 | "lat": 40.4936, 80 | "lon": -3.5668 81 | }, 82 | "station": { 83 | "id": 5488, 84 | "name": "LEMD", 85 | "status": 50, 86 | "type": 1 87 | } 88 | }, 89 | { 90 | "id": 4, 91 | "stationId": 187956, 92 | "distance": 20.445, 93 | "dt": 1469793737, 94 | "main": { 95 | "humidity": 29, 96 | "pressure": 1014, 97 | "temp": 307.59 98 | }, 99 | "rain": { 100 | "1h": 0, 101 | "24h": 0, 102 | "today": 0 103 | }, 104 | "wind": { 105 | "deg": 172, 106 | "gust": 0, 107 | "speed": 0 108 | }, 109 | "coord": { 110 | "lat": 40.4467, 111 | "lon": -3.461 112 | }, 113 | "station": { 114 | "id": 187956, 115 | "name": "EA4CQL-11", 116 | "status": 20, 117 | "type": 2 118 | } 119 | }, 120 | { 121 | "id": 5, 122 | "stationId": 120400, 123 | "distance": 20.445, 124 | "dt": 1452255851, 125 | "main": { 126 | "humidity": 66, 127 | "pressure": 1003, 128 | "temp": 288.15 129 | }, 130 | "rain": { 131 | "1h": 0, 132 | "24h": 0.508, 133 | "today": 7.62 134 | }, 135 | "wind": { 136 | "deg": 154, 137 | "gust": 0.51, 138 | "speed": 0 139 | }, 140 | "coord": { 141 | "lat": 40.4467, 142 | "lon": -3.461 143 | }, 144 | "station": { 145 | "id": 120400, 146 | "name": "EA4CQL-11", 147 | "status": 20, 148 | "type": 2 149 | } 150 | } 151 | ] 152 | -------------------------------------------------------------------------------- /server/enviroment/development.js: -------------------------------------------------------------------------------- 1 | export const port = 8000; 2 | export const sslPort = 443; -------------------------------------------------------------------------------- /server/enviroment/index.js: -------------------------------------------------------------------------------- 1 | import { env } from 'base'; 2 | import * as development from './development'; 3 | import * as production from './production'; 4 | 5 | const envConf = (env === 'production') ? production : development; 6 | 7 | export default envConf; 8 | -------------------------------------------------------------------------------- /server/enviroment/production.js: -------------------------------------------------------------------------------- 1 | export const port = 8000; 2 | export const sslPort = 443; -------------------------------------------------------------------------------- /server/httpServer.js: -------------------------------------------------------------------------------- 1 | import base from 'base/'; 2 | import envConf from './enviroment'; 3 | 4 | const httpServer = app => { 5 | app.listen(envConf.port, (err) => { 6 | if (err) return base.console.error(`${err}`); 7 | base.console.success(`HTTP Server up on http://localhost:${envConf.port}`); 8 | }); 9 | }; 10 | 11 | export default httpServer; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | const base = require('../src/base').default; 3 | 4 | require('css-modules-require-hook')({ 5 | rootDir: './', 6 | mode: 'local', 7 | generateScopedName: (base.env === 'development') ? '[local]-[hash:base64:4]' : '[hash:base64:4]', 8 | }); 9 | 10 | base.console.info(`Starting ${base.env} enviroment...`); 11 | require("./server"); 12 | -------------------------------------------------------------------------------- /server/lib/configureStore.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, createStore } from 'redux'; 2 | import reduxReqMiddleware from 'redux-req-middleware'; 3 | import rootReducer from '../../src/base/reducers/'; 4 | 5 | export default function configureServerStore() { 6 | const configureStore = applyMiddleware(reduxReqMiddleware())(createStore); 7 | return configureStore(rootReducer); 8 | } 9 | -------------------------------------------------------------------------------- /server/lib/files.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { env } from '../../src/base/'; 4 | import * as FileSystem from '../../src/base/shared/FileSystem'; 5 | 6 | let files; 7 | 8 | if (env === 'development') { 9 | files = { 10 | app: { js: '/app.js' }, 11 | vendor: { js: '/dlls/vendor.dll.js' }, 12 | }; 13 | } else { 14 | const assetsManifest = path.resolve(__dirname, '../../dist/webpack-assets.json'); 15 | files = JSON.parse(FileSystem.readFile(assetsManifest, 'utf8')); 16 | } 17 | 18 | export function getScripts(file) { 19 | const scriptPath = `${files[file].js}`; 20 | return (env === 'production') ? `` : ``; 21 | } 22 | 23 | export function getStyles(file) { 24 | const cssPath = files[file].css; 25 | return (env === 'production') ? `` : ''; 26 | } 27 | -------------------------------------------------------------------------------- /server/lib/renderContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { RouterContext } from 'react-router'; 4 | import { renderToString } from 'react-dom/server'; 5 | 6 | export default function renderContainer(store, renderProps) { 7 | 8 | return renderToString( 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /server/lib/renderPage.js: -------------------------------------------------------------------------------- 1 | import * as templates from '../templates/'; 2 | import { getScripts, getStyles } from '../lib/files'; 3 | 4 | export default function renderPage(routeMatch, container, store) { 5 | 6 | const params = { 7 | title: params, 8 | container: container, 9 | routeMatch: routeMatch, 10 | state: store.getState(), 11 | style: getStyles('app'), 12 | appScript: getScripts('app'), 13 | vendorScript: getScripts('vendor') 14 | }; 15 | 16 | let template; 17 | let route = routeMatch.substring(1).split('/'); 18 | if (route.length === 1) template = templates[route]; 19 | else { 20 | if (route[0] === 'post') template = templates.post; 21 | else template = templates.tag; 22 | } 23 | 24 | if (routeMatch === '/' || !template) return templates.main(params); 25 | else return template(params); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/middleware/dev-middleware.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import webpackDevMiddleware from 'webpack-dev-middleware'; 3 | import webpackHotMiddleware from 'webpack-hot-middleware'; 4 | 5 | import base from '../../src/base/'; 6 | 7 | const config = require('../../webpack/index.babel.js'); 8 | 9 | const compiler = webpack(config); 10 | 11 | const serverOptions = { 12 | hot: true, 13 | lazy: false, 14 | quiet: true, 15 | watch: true, 16 | noInfo: true, 17 | inline: true, 18 | progress: false, 19 | stats: { colors: true }, 20 | publicPath: config.output.publicPath, 21 | headers: { 22 | 'Access-Control-Allow-Origin': '*', 23 | 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 24 | 'Access-Control-Allow-Headers': 'X-Requested-With , Accept-Language, Content-type, Authorization' 25 | } 26 | }; 27 | 28 | let bundleStart = Date.now(); 29 | 30 | compiler.plugin("compile", function() { 31 | bundleStart = Date.now(); 32 | }); 33 | 34 | compiler.plugin('done', function() { 35 | base.console.success(`Bundled project in ${Date.now() - bundleStart} ms!`); 36 | }); 37 | 38 | const applyDevMiddleware = function() { 39 | return [ 40 | webpackHotMiddleware(compiler, { log:false, noInfo: true, quiet: true}), 41 | webpackDevMiddleware(compiler, serverOptions), 42 | ]; 43 | }; 44 | 45 | module.exports = applyDevMiddleware; 46 | -------------------------------------------------------------------------------- /server/middleware/index.js: -------------------------------------------------------------------------------- 1 | import base from 'base'; 2 | 3 | const envMiddleware = (base.env === 'development') ? require('./dev-middleware') : require('./prod-middleware'); 4 | 5 | export default function applyEnvMiddleWare(app) { 6 | 7 | base.console.info(`Checking Env middlewares...`); 8 | 9 | return new Promise((resolve) => { 10 | let serverUp = false; 11 | 12 | envMiddleware().forEach(function(middleware) { 13 | const middlewareName = middleware.name || 'middleware'; 14 | app.use(middleware); 15 | 16 | if (base.env == 'production' && !serverUp) { 17 | serverUp = true; 18 | base.console.success(`Applied ${middlewareName} middleware`); 19 | resolve(true); 20 | } else { 21 | if (middleware.waitUntilValid) { 22 | middleware.waitUntilValid(function() { 23 | base.console.success(`Applied ${middlewareName} middleware`); 24 | resolve(true); 25 | }); 26 | } 27 | } 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /server/middleware/prod-middleware.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import helmet from 'helmet'; 3 | import compression from 'compression'; 4 | 5 | import base from 'base'; 6 | 7 | const config = require('../../webpack/index.babel.js'); 8 | 9 | const compiler = webpack(config); 10 | let bundleStart = Date.now(); 11 | 12 | compiler.plugin("compile", function() { 13 | base.console.info(`Bundling project...`); 14 | bundleStart = Date.now(); 15 | }); 16 | 17 | compiler.plugin('done', function() { 18 | base.console.success(`Bundled project in ${Date.now() - bundleStart} ms!`); 19 | }); 20 | 21 | const allowCrossDomain = function(req, res, next) { 22 | res.header('Access-Control-Allow-Origin', '*'); 23 | res.header('Access-Control-Allow-Methods', 'GET,OPTIONS'); 24 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 25 | next(); 26 | }; 27 | 28 | const applyProdMiddleware = () => { 29 | const middleware = [ 30 | helmet(), 31 | compression(), 32 | allowCrossDomain 33 | ]; 34 | return middleware; 35 | }; 36 | 37 | module.exports = applyProdMiddleware; -------------------------------------------------------------------------------- /server/middleware/routing-middleware.js: -------------------------------------------------------------------------------- 1 | import { match } from 'react-router'; 2 | 3 | import { fetchRequiredActions, context } from 'base'; 4 | import routes from '../../src/base/routes'; 5 | import renderPage from '../lib/renderPage'; 6 | import renderContainer from '../lib/renderContainer'; 7 | import configureServerStore from '../lib/configureStore'; 8 | 9 | export default function routingMiddleware(req, res) { 10 | 11 | const store = configureServerStore(); 12 | 13 | match({ routes , location: req.url }, (error, redirectLocation, renderProps) => { 14 | if (error) return res.status(500).send(error.message); 15 | if (redirectLocation) return res.redirect(302, redirectLocation.pathname + redirectLocation.search); 16 | if (renderProps == null) return res.status(404).send('Not found'); 17 | fetchRequiredActions(store.dispatch, renderProps.components, renderProps.params, context.context) 18 | .then(() => { 19 | const routeMatch = renderProps.location.pathname; 20 | const renderedContainer = renderContainer(store, renderProps); 21 | const page = renderPage(routeMatch, renderedContainer, store); 22 | return res.status(200).send(page); 23 | }) 24 | .catch(err => res.end(err.message)); 25 | 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /server/routing.js: -------------------------------------------------------------------------------- 1 | import base from '../src/base/'; 2 | import routingMiddleware from './middleware/routing-middleware'; 3 | 4 | const applyServerRouting = (app) => { 5 | 6 | app.use(routingMiddleware); 7 | base.console.success(`Routing up`); 8 | }; 9 | 10 | export default applyServerRouting; 11 | 12 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import base from 'base'; 4 | import httpServer from './httpServer'; 5 | import applyStaticsPaths from './statics'; 6 | import applyServerRouting from './routing'; 7 | import applyEnvMiddleWare from './middleware'; 8 | 9 | const app = express(); 10 | 11 | const launchServer = () => { 12 | applyEnvMiddleWare(app) 13 | .then(() => { 14 | base.console.info(`Checking static paths...`); 15 | applyStaticsPaths(app); 16 | }) 17 | .then(() => { 18 | base.console.info(`Checking server routing...`); 19 | applyServerRouting(app); 20 | }) 21 | .then(() => { 22 | base.console.info(`Setting up server...`); 23 | httpServer(app); 24 | }) 25 | .catch((e) => { 26 | base.console.error(`Server Error ${e}...`); 27 | }); 28 | }; 29 | 30 | launchServer(); -------------------------------------------------------------------------------- /server/statics/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import express from 'express'; 3 | 4 | import base from 'base'; 5 | 6 | const commonStatics = [ 7 | {route: '/mocks', dir: path.join(__dirname, '../../server/api/mocks')} 8 | ]; 9 | 10 | const devStatics = [ 11 | {route: '/dlls', dir: path.join(__dirname, '../../dist/')}, 12 | {route: '/', dir: path.join(__dirname, '../../src/app')}, 13 | ]; 14 | 15 | const prodStatics = [ 16 | {route: '/', dir: path.join(__dirname, '../../dist')}, 17 | {route: '/assets', dir: path.join(__dirname, '../../dist/assets')}, 18 | ]; 19 | 20 | const envStatics = (base.env === 'development') ? commonStatics.concat(devStatics) : commonStatics.concat(prodStatics); 21 | const statics = envStatics; 22 | 23 | export default function applyStaticsPaths(app) { 24 | statics.map(function(staticPath) { 25 | app.use(staticPath.route, express.static(staticPath.dir)); 26 | base.console.success(`Applied static path ${staticPath.route}`); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /server/templates/calculator.js: -------------------------------------------------------------------------------- 1 | 2 | export default function calculator(params) { 3 | 4 | const state = JSON.stringify(params.state); 5 | 6 | return ` 7 | 8 | 9 | 10 | 11 | Simple Redux Calculator 12 | ${ params.vendorScript } 13 | ${ params.style } 14 | 15 | 16 |
${ params.container }
17 | 18 | ${ params.appScript } 19 | 20 | 21 | `; 22 | } 23 | -------------------------------------------------------------------------------- /server/templates/index.js: -------------------------------------------------------------------------------- 1 | import main from './main'; 2 | import calculator from './calculator'; 3 | 4 | export { main, calculator }; 5 | -------------------------------------------------------------------------------- /server/templates/main.js: -------------------------------------------------------------------------------- 1 | 2 | export default function main(params) { 3 | 4 | const state = JSON.stringify(params.state); 5 | 6 | return ` 7 | 8 | 9 | 10 | 11 | React Base 12 | ${ params.vendorScript } 13 | ${ params.style } 14 | 15 | 16 |
${ params.container }
17 | 18 | ${ params.appScript } 19 | 20 | 21 | `; 22 | } 23 | -------------------------------------------------------------------------------- /src/app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atSistemas/react-base/d594a3538695f0ac9c45179dbd5d69f728e40b52/src/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /src/app/assets/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/app/assets/images/react-base-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atSistemas/react-base/d594a3538695f0ac9c45179dbd5d69f728e40b52/src/app/assets/images/react-base-logo.png -------------------------------------------------------------------------------- /src/app/components/LinkButton/index.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router'; 2 | import React from 'react'; 3 | import { PropTypes } from 'prop-types'; 4 | import styles from './styles.css'; 5 | 6 | const propTypes = { 7 | value: PropTypes.string.isRequired, 8 | location: PropTypes.string.isRequired, 9 | }; 10 | 11 | const LinkButton = props => { 12 | return ( 13 | 14 | 17 | 18 | ); 19 | }; 20 | 21 | LinkButton.propTypes = propTypes; 22 | 23 | export default LinkButton; 24 | -------------------------------------------------------------------------------- /src/app/components/LinkButton/spec/LinkButton.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { expect } from 'chai'; 3 | import { mount, shallow } from 'enzyme'; 4 | 5 | import LinkButton from '../'; 6 | 7 | describe('Components / LinkButton', () => { 8 | describe('', () => { 9 | 10 | it('Should has properties', () => { 11 | 12 | const component = mount(); 13 | expect(component.props().location).to.equal("/calculator"); 14 | expect(component.props().value).to.equal("Simple calculator"); 15 | 16 | }); 17 | 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/app/components/LinkButton/styles.css: -------------------------------------------------------------------------------- 1 | .LinkButton { 2 | &:last-child { 3 | margin-left: 30px; 4 | } 5 | 6 | button { 7 | background: #47BBEA; 8 | border-radius: 4px; 9 | border: 1px solid transparent; 10 | color: #ffffff; 11 | cursor: pointer; 12 | font-family: Arial; 13 | font-size: 14px; 14 | font-weight: 500; 15 | padding: .5rem 1rem; 16 | text-decoration: none; 17 | width: 100%; 18 | 19 | &:hover { 20 | background: #3cb0fd; 21 | border: 1px solid transparent; 22 | text-decoration: none; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/app/components/Logo/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | import { Link } from 'react-router'; 4 | 5 | const propTypes= { 6 | alt: PropTypes.string.isRequired, 7 | src: PropTypes.string.isRequired, 8 | width: PropTypes.number.isRequired 9 | }; 10 | 11 | const ReactBaseLogo = props => ( 12 | 13 | 14 | { 15 | 16 | ); 17 | 18 | ReactBaseLogo.propTypes= propTypes; 19 | 20 | export default ReactBaseLogo; 21 | -------------------------------------------------------------------------------- /src/app/components/Logo/styles.css: -------------------------------------------------------------------------------- 1 | .main { 2 | height: 100%; 3 | display: table-cell; 4 | vertical-align: middle; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/containers/App/index.jsx: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | 4 | class App extends Component { 5 | 6 | static propTypes = { 7 | children: PropTypes.object.isRequired 8 | } 9 | 10 | render() { 11 | const children = this.props.children; 12 | return ( 13 |
14 |
15 | { children } 16 |
17 |
18 | ); 19 | 20 | } 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /src/app/containers/App/spec/App.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import React from 'react'; 3 | import ReactShallowRenderer from 'react-test-renderer/shallow'; 4 | import App from '../'; 5 | 6 | function setup() { 7 | let props = { 8 | children: {} 9 | }; 10 | 11 | let renderer = new ReactShallowRenderer(); 12 | renderer.render(); 13 | let output = renderer.getRenderOutput(); 14 | 15 | return { 16 | props, 17 | output, 18 | renderer 19 | }; 20 | } 21 | 22 | describe('comtainers', () => { 23 | describe('App comtainer', () => { 24 | it('should render correctly', () => { 25 | const { output } = setup(); 26 | expect(output.type).to.equal('div'); 27 | 28 | const main = output.props.children; 29 | expect(main.type).to.equal('main'); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/app/containers/Calculator/actionTypes/index.js: -------------------------------------------------------------------------------- 1 | import { createActionType } from 'base'; 2 | 3 | export default createActionType([ 4 | 'SUM', 5 | 'CLEAN', 6 | 'RESULT', 7 | 'DIVIDE', 8 | 'PERCENT', 9 | 'MULTIPLY', 10 | 'SUBSTRACT', 11 | 'CHANGE_SIGN', 12 | 'INPUT_NUMBER', 13 | 'INPUT_DECIMAL', 14 | 'INPUT_OPERATOR', 15 | 'INPUT_OPERATION' 16 | ]); 17 | -------------------------------------------------------------------------------- /src/app/containers/Calculator/actions/index.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../actionTypes'; 2 | 3 | export const inputNumber = value => ({ 4 | type: ActionTypes.INPUT_NUMBER, 5 | payload: { value } 6 | }); 7 | 8 | export const inputOperation = value => ({ 9 | type: ActionTypes.INPUT_OPERATION, 10 | payload: { value } 11 | }); 12 | 13 | export const inputDecimal = () => ({ 14 | type: ActionTypes.INPUT_DECIMAL 15 | }); 16 | 17 | export const inputOperator = operator => ({ 18 | type: ActionTypes.INPUT_OPERATOR, 19 | payload: { operator } 20 | }); 21 | 22 | export const result = operator => ({ 23 | type: ActionTypes.RESULT, 24 | payload: { operator } 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/containers/Calculator/actions/spec/Calculator.actions.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as Actions from '../'; 3 | import ActionTypes from '../../actionTypes'; 4 | 5 | describe('Calculator', () => { 6 | 7 | describe('Actions', () => { 8 | 9 | it('Should get all Actions', () => { 10 | 11 | let expected; 12 | 13 | const inputNumber = Actions.inputNumber(3); 14 | expected = {type: ActionTypes.INPUT_NUMBER, payload: {value: 3 }}; 15 | expect(expected).to.deep.equal(inputNumber); 16 | 17 | const inputOperation = Actions.inputOperation(ActionTypes.CLEAN); 18 | expected = {type: ActionTypes.INPUT_OPERATION, payload: {value: ActionTypes.CLEAN}}; 19 | expect(expected).to.deep.equal(inputOperation); 20 | 21 | const inputDecimal = Actions.inputDecimal(); 22 | expected = {type: ActionTypes.INPUT_DECIMAL}; 23 | expect(expected).to.deep.equal(inputDecimal); 24 | 25 | const inputOperator = Actions.inputOperator(ActionTypes.SUM); 26 | expected = {type: ActionTypes.INPUT_OPERATOR, payload: {operator: ActionTypes.SUM}}; 27 | expect(expected).to.deep.equal(inputOperator); 28 | 29 | }); 30 | 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/app/containers/Calculator/components/Button/index.jsx: -------------------------------------------------------------------------------- 1 | import Ink from 'react-ink'; 2 | import React from 'react'; 3 | import { PropTypes } from 'prop-types'; 4 | 5 | import styles from './styles.css'; 6 | 7 | const propTypes = { 8 | type: PropTypes.string.isRequired, 9 | value: PropTypes.string.isRequired, 10 | onClick: PropTypes.func.isRequired 11 | }; 12 | 13 | const Button = props => { 14 | let style; 15 | 16 | if (props.type === 'operator') style = styles.ButtonOperate; 17 | else if (props.type === 'zero') style = styles.ButtonZero; 18 | else style = styles.Button; 19 | 20 | return ( 21 | 32 | ); 33 | }; 34 | 35 | Button.propTypes= propTypes; 36 | 37 | export default Button; 38 | -------------------------------------------------------------------------------- /src/app/containers/Calculator/components/Button/spec/Button.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { expect } from 'chai'; 3 | import { mount, shallow } from 'enzyme'; 4 | 5 | import Button from '../'; 6 | 7 | describe('Calculator / Components', () => { 8 | describe('