├── .dockerignore ├── .eslintrc ├── .gitignore ├── Dockerfile ├── README.md ├── package-lock.json ├── package.json ├── src ├── components │ ├── App.js │ ├── Clicker.js │ └── Header.js ├── favicon.ico ├── index.html ├── index.js └── styles │ ├── _base.scss │ ├── _bootstrap.scss │ ├── _settings.scss │ ├── app.scss │ └── components │ ├── _clicker.scss │ └── _header.scss └── webpack.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | README.md 4 | /public/* 5 | /node_modules/* -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "commonjs": true, 6 | "es6": true, 7 | "node": true 8 | }, 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:react/recommended" 12 | ], 13 | "parserOptions": { 14 | "ecmaVersion": 6, 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "sourceType": "module" 19 | }, 20 | "plugins": [ 21 | "react" 22 | ], 23 | "rules": { 24 | "no-console": "off", 25 | "indent": [ 26 | "error", 27 | 4 28 | ], 29 | "quotes": [ 30 | "error", 31 | "single" 32 | ], 33 | "semi": [ 34 | "error", 35 | "always" 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /public 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4-alpine 2 | LABEL maintainer="Douglas Minnaar" 3 | LABEL description="A simple React app that allows one to increase or decrease a counter" 4 | EXPOSE 8080 5 | RUN apk update && \ 6 | apk upgrade && \ 7 | rm -rf /var/cache/apk/* && \ 8 | mkdir -p /usr/src/app 9 | WORKDIR /usr/src/app 10 | COPY . . 11 | RUN npm install --silent && npm cache clean --force 12 | CMD [ "npm", "start"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Clicker 2 | 3 | A basic React app that allows one to increase, decrease, or reset a counter. 4 | 5 | Go **[here](http://react-clicker.drminnaar.me/)** for live demo. 6 | 7 | This project also demonstrates: 8 | 9 | * a typcial React project layout structure 10 | * babel setup and configuration 11 | * webpack setup and configuration 12 | * eslint setup and configuration 13 | * SCSS setup and configuration 14 | * How to run application in a Docker container 15 | 16 | ... | ... 17 | --- | --- 18 | ![](https://user-images.githubusercontent.com/33935506/33752825-a81c5ed0-dbec-11e7-8041-44d1a5b2ca46.PNG) | ![](https://user-images.githubusercontent.com/33935506/33752826-a857c722-dbec-11e7-8eb8-12375f840d10.PNG) 19 | 20 | --- 21 | 22 | ## Developed With 23 | 24 | * [Node.js](https://nodejs.org/en/) - Javascript runtime 25 | * [React](https://reactjs.org/) - A javascript library for building user interfaces 26 | * [Babel](https://babeljs.io/) - A transpiler for javascript 27 | * [Webpack](https://webpack.js.org/) - A module bundler 28 | * [SCSS](http://sass-lang.com/) - A css metalanguage 29 | * [Bootstrap 4](https://getbootstrap.com/) - Bootstrap is an open source toolkit for developing with HTML, CSS, and JS 30 | * [Surge] - Static web publishing for Front-End Developers 31 | * [Docker] - A container management system 32 | 33 | --- 34 | 35 | ## Related Projects 36 | 37 | * [react-starter] 38 | 39 | A basic template that consists of the essential elements that are required to start building a React application 40 | 41 | * [react-clock-basic] 42 | 43 | A basic clock that displays the current date and time 44 | 45 | * [react-timer-basic] 46 | 47 | A basic timer that will start a countdown based on an input of time in seconds 48 | 49 | * [react-timer-advanced] 50 | 51 | A basic countdown timer that offers an advanced UI experience 52 | 53 | * [react-masterminds] 54 | 55 | A basic game of guessing a number with varying degrees of difficulty 56 | 57 | * [react-movie-cards] 58 | 59 | A basic application that displays a list of movies as a list of cards 60 | 61 | * [react-calculator-standard] 62 | 63 | A calculator that provides the essential arithmetic operations, an expression builder, and a complete history of all expressions 64 | 65 | * [react-bitcoin-monitor] 66 | 67 | An app that monitors changes in the Bitcoin Price Index (BPI) 68 | 69 | * [react-weather-standard] 70 | 71 | A weather application that displays the current weather, daily forecasts, and hourly forecasts based on your current geolocation 72 | 73 | --- 74 | 75 | ## Getting Started 76 | 77 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 78 | 79 | ### Prerequisites 80 | 81 | The following software is required to be installed on your system: 82 | 83 | * Node 8.x 84 | * Npm 3.x 85 | 86 | Type the following commands in the terminal to verify your node and npm versions 87 | 88 | ```bash 89 | node -v 90 | npm -v 91 | ``` 92 | 93 | ### Install 94 | 95 | Follow the following steps to get development environment running. 96 | 97 | * Clone _'react-clicker'_ repository from GitHub 98 | 99 | ```bash 100 | git clone https://github.com/drminnaar/react-clicker.git 101 | ``` 102 | 103 | _OR USING SSH_ 104 | 105 | ```bash 106 | git clone git@github.com:drminnaar/react-clicker.git 107 | ``` 108 | 109 | * Install node modules 110 | 111 | ```bash 112 | cd react-clicker 113 | npm install 114 | ``` 115 | 116 | ### Build 117 | 118 | * Build application 119 | 120 | This command will also run ESLint as part of build process. 121 | 122 | ```bash 123 | npm run build 124 | ``` 125 | 126 | * Build application and start watching for changes 127 | 128 | This command will also run ESLint as part of build process. 129 | 130 | ```bash 131 | npm run build:watch 132 | ``` 133 | 134 | ### Run ESlint 135 | 136 | * Lint project using ESLint 137 | 138 | ```bash 139 | npm run lint 140 | ``` 141 | 142 | * Lint project using ESLint, and autofix 143 | 144 | ```bash 145 | npm run lint:fix 146 | ``` 147 | 148 | ### Run 149 | 150 | * Run start 151 | 152 | This will run the _'serve'_ npm task 153 | 154 | ```bash 155 | npm start 156 | ``` 157 | 158 | * Run webpack dev server 159 | 160 | ```bash 161 | npm run serve:dev 162 | ``` 163 | 164 | * Alternatively run live-server (simple development http server with live reload capability) 165 | 166 | ```bash 167 | npm run serve 168 | ``` 169 | 170 | ### Docker Instructions 171 | 172 | An alternative to installing and running application on your local machine is to build and run your own Docker container that will host the application. There are 2 files related to Docker setup namely: 173 | 174 | * Dockerfile - Used to create Docker Image 175 | 176 | * .dockerignore - Used to ignore files in local path that are not required in the container 177 | 178 | To get the application up and running in a Docker container, please follow the following instructions: 179 | 180 | 1. Build Docker Image 181 | 182 | The following command will build a new Docker Image called _'react-clicker'_ with the tag _'1.0.0'_ using the Docker file found in the application root. 183 | 184 | ```docker 185 | docker build -t react-clicker:1.0.0 . 186 | ``` 187 | 188 | 1. Run Docker Container 189 | 190 | The following command will start a new container based on the Docker image created above. The application within the container runs on port 8080, therefore, as part of command we map container port to local host port. We also give the container a name and run it in the background. The '--rm' switch indicates that the container will be automatically removed once it is stopped. 191 | 192 | ```docker 193 | docker run --rm --name react-clicker -p 8080:8080 -d react-clicker:1.0.0 194 | ``` 195 | 196 | 1. Open application 197 | 198 | By typing the following command, you should see a runing container having the name _'react-clciker'_. 199 | 200 | ```docker 201 | docker container ls 202 | ``` 203 | 204 | If the container is running, you may navigate to the application using your browser at the following address: 205 | 206 | ```bash 207 | http://localhost:8080 208 | ``` 209 | 210 | Lastly, if you're curious to have a look inside container, then you may type the following command: 211 | 212 | ```docker 213 | docker exec -it react-clicker /bin/sh 214 | ``` 215 | 216 | The above command provides you an interactive shell on the container. Please note that the above command must be executed on a running container. 217 | 218 | --- 219 | 220 | ## Versioning 221 | 222 | I use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/drminnaar/react-clicker/tags). 223 | 224 | ## Authors 225 | 226 | * **Douglas Minnaar** - *Initial work* - [drminnaar](https://github.com/drminnaar) 227 | 228 | [Surge]: https://surge.sh/ 229 | [Docker]: https://www.docker.com/ 230 | [react-starter]: https://github.com/drminnaar/react-starter 231 | [react-clicker]: https://github.com/drminnaar/react-clicker 232 | [react-clock-basic]: https://github.com/drminnaar/react-clock-basic 233 | [react-timer-basic]: https://github.com/drminnaar/react-timer-basic 234 | [react-timer-advanced]: https://github.com/drminnaar/react-timer-advanced 235 | [react-masterminds]: https://github.com/drminnaar/react-masterminds 236 | [react-movie-cards]: https://github.com/drminnaar/react-movie-cards 237 | [react-calculator-standard]: https://github.com/drminnaar/react-calculator-standard 238 | [react-bitcoin-monitor]: https://github.com/drminnaar/react-bitcoin-monitor 239 | [react-weather-standard]: https://github.com/drminnaar/react-weather-standard -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-clicker", 3 | "version": "1.0.0", 4 | "description": "A simple app that allows one to increase or decrease a counter", 5 | "scripts": { 6 | "build": "webpack -d", 7 | "build:watch": "webpack -dw", 8 | "lint": "eslint ./webpack.config.js ./src/*.js ./src/**/*.js; exit 0", 9 | "lint:fix": "eslint ./webpack.config.js ./src/*.js ./src/**/*.js --fix", 10 | "serve": "webpack -p && live-server ./public", 11 | "serve:dev": "webpack-dev-server --open", 12 | "start": "npm run serve", 13 | "test": "echo \"No tests available\"" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/drminnaar/react-clicker.git" 18 | }, 19 | "babel": { 20 | "presets": [ 21 | "env", 22 | "react" 23 | ] 24 | }, 25 | "devDependencies": { 26 | "babel-cli": "^6.26.0", 27 | "babel-core": "^6.26.0", 28 | "babel-eslint": "^8.2.1", 29 | "babel-loader": "^7.1.2", 30 | "babel-preset-env": "^1.6.1", 31 | "babel-preset-react": "^6.24.1", 32 | "clean-webpack-plugin": "^0.1.18", 33 | "css-loader": "^0.28.9", 34 | "eslint": "^4.17.0", 35 | "eslint-loader": "^1.9.0", 36 | "eslint-plugin-react": "^7.6.1", 37 | "html-webpack-plugin": "^2.30.1", 38 | "live-server": "^1.2.0", 39 | "node-sass": "^4.7.2", 40 | "sass-loader": "^6.0.6", 41 | "style-loader": "^0.20.1", 42 | "url-loader": "^0.6.2", 43 | "webpack": "^3.10.0", 44 | "webpack-dev-server": "^2.11.1" 45 | }, 46 | "dependencies": { 47 | "prop-types": "^15.6.0", 48 | "react": "^16.2.0", 49 | "react-dom": "^16.2.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Header from './Header'; 3 | import Clicker from './Clicker'; 4 | 5 | export default class ClickerApp extends Component { 6 | 7 | constructor() { 8 | super(); 9 | 10 | this.state = { 11 | title: 'React Clicker' 12 | }; 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
19 |
20 | 21 |
22 |
23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Clicker.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class Clicker extends Component { 5 | 6 | constructor(props) { 7 | super(); 8 | 9 | this.state = { 10 | count: props.count 11 | }; 12 | 13 | this.incrementCount = this.incrementCount.bind(this); 14 | this.decrementCount = this.decrementCount.bind(this); 15 | this.resetCount = this.resetCount.bind(this); 16 | } 17 | 18 | incrementCount() { 19 | this.setState((prevState) => ({ count: prevState.count + 1 })); 20 | } 21 | 22 | decrementCount() { 23 | this.setState((prevState) => ({ count: prevState.count - 1 })); 24 | } 25 | 26 | resetCount() { 27 | this.setState(() => ({ count: 0 })); 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 |
34 |
35 |
{this.state.count}
36 |
37 |
38 | 41 | 44 | 47 |
48 |
49 |
50 | ); 51 | } 52 | } 53 | 54 | Clicker.defaultProps = { 55 | count: 0 56 | }; 57 | 58 | Clicker.propTypes = { 59 | count: PropTypes.number 60 | }; 61 | 62 | export default Clicker; -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Header = (props) => ( 5 | 13 | ); 14 | 15 | Header.defaultProps = { 16 | title: 'Title' 17 | }; 18 | 19 | Header.propTypes = { 20 | title: PropTypes.string 21 | }; 22 | 23 | export default Header; -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drminnaar/react-clicker/aca6b849fff8e6bc5ba12478a8af315c6d88ba03/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Clicker 9 | 10 | 12 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | import './styles/app.scss'; 6 | 7 | ReactDOM.render(, document.getElementById('app')); -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: $font-family; 3 | } -------------------------------------------------------------------------------- /src/styles/_bootstrap.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | border-radius: 0 !important; 3 | cursor: pointer; 4 | } -------------------------------------------------------------------------------- /src/styles/_settings.scss: -------------------------------------------------------------------------------- 1 | $font-family: 'Open Sans', Helvetica, Arial, sans-serif; -------------------------------------------------------------------------------- /src/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import './_settings.scss'; 2 | @import './_base.scss'; 3 | @import './_bootstrap.scss'; 4 | @import './components/_clicker.scss'; 5 | @import './components/_header.scss'; -------------------------------------------------------------------------------- /src/styles/components/_clicker.scss: -------------------------------------------------------------------------------- 1 | .clicker { 2 | height: 300px; 3 | width: 300px; 4 | margin: 0 auto; 5 | overflow: hidden; 6 | } 7 | 8 | .clicker-display { 9 | height: 200px; 10 | } 11 | 12 | .clicker-button-panel { 13 | height: 100px; 14 | } -------------------------------------------------------------------------------- /src/styles/components/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 100px; 3 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebPackPlugin = require('html-webpack-plugin'); 3 | const CleanWebPackPlugin = require('clean-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | output: { 8 | filename: 'bundle.js', 9 | path: path.join(__dirname, 'public') 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | enforce: 'pre', 15 | test: /\.js$/, 16 | loader: 'eslint-loader', 17 | options: { 18 | failOnWarning: true, 19 | failOnError: true 20 | }, 21 | exclude: /node_modules/ 22 | }, 23 | { 24 | test: /\.js$/, 25 | loader: 'babel-loader', 26 | exclude: /node_modules/ 27 | }, 28 | { 29 | test: /\.svg|png|jpg$/, 30 | loader: 'url-loader', 31 | exclude: /node_modules/ 32 | }, 33 | { 34 | test: /\.s?css$/, 35 | use: [ 36 | 'style-loader', 37 | 'css-loader', 38 | 'sass-loader' 39 | ] 40 | } 41 | ] 42 | }, 43 | plugins: [ 44 | new CleanWebPackPlugin( [ 'public' ], path.resolve(__dirname)), 45 | new HtmlWebPackPlugin({ 46 | template: './src/index.html', 47 | favicon: './src/favicon.ico', 48 | inject: false 49 | }) 50 | ], 51 | devtool: 'cheap-module-eval-source-map', 52 | devServer: { 53 | contentBase: path.join(__dirname, 'public'), 54 | compress: true, 55 | port: 9000 56 | } 57 | }; --------------------------------------------------------------------------------