├── .gitignore ├── README.md ├── basic-react-redux ├── .babelrc ├── LICENSE ├── package.json ├── src │ ├── index.html │ └── js │ │ ├── actions │ │ └── index.js │ │ ├── components │ │ ├── App.js │ │ ├── Form.js │ │ └── List.js │ │ ├── constants │ │ └── action-types.js │ │ ├── index.js │ │ ├── reducers │ │ ├── articleReducer.js │ │ └── index.js │ │ └── store │ │ └── index.js └── webpack.config.js ├── basic-redux-thunk ├── nwb.config.js ├── package.json └── src │ ├── actions │ └── items.js │ ├── components │ └── ItemList.js │ ├── index.html │ ├── index.js │ ├── reducers │ ├── index.js │ └── items.js │ └── store │ └── configureStore.js ├── book-store ├── .babelrc ├── .editorconfig ├── .eslintrc ├── .github │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── .istanbul.yml ├── .npmrc ├── .travis.yml ├── appveyor.yml ├── docs │ └── FAQ.md ├── package.json ├── src │ ├── actions │ │ └── bookActions.js │ ├── components │ │ ├── App.js │ │ ├── book │ │ │ └── BookPage.js │ │ ├── common │ │ │ ├── AboutPage.js │ │ │ └── HomePage.js │ │ └── emptyTest.spec.js │ ├── favicon.ico │ ├── index.ejs │ ├── index.js │ ├── reducers │ │ ├── bookReducers.js │ │ └── index.js │ ├── routes.js │ ├── store │ │ └── configureStore.js │ └── webpack-public-path.js ├── tools │ ├── .yarnclean │ ├── build.js │ ├── chalkConfig.js │ ├── distServer.js │ ├── nodeVersionCheck.js │ ├── removeDemo.js │ ├── srcServer.js │ ├── startMessage.js │ └── testSetup.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── yarn.lock ├── gatsbyjs-markdown-blog ├── .gitignore ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-node.js ├── package.json └── src │ ├── layouts │ └── index.js │ ├── pages │ ├── blog.js │ ├── index.js │ ├── pandas-and-bananas.md │ └── sweet-pandas-eating-sweets.md │ ├── templates │ └── blog-post.js │ └── utils │ └── typography.js ├── markdown-editor ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── index.css │ └── index.js └── yarn.lock ├── property-finder ├── App.js ├── App.test.js ├── README.md ├── app.json ├── package.json ├── resources │ └── house-icon.png ├── screens │ ├── SearchPage.js │ └── SearchResults.js └── yarn.lock ├── reddit-clone ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── actions.js │ ├── actions │ └── index.js │ ├── components │ ├── Picker.js │ └── Posts.js │ ├── configureStore.js │ ├── constants.js │ ├── constants │ └── action-types.js │ ├── containers │ ├── AsyncApp.js │ └── Root.js │ ├── index.js │ ├── reducers.js │ ├── reducers │ └── index.js │ └── registerServiceWorker.js ├── tictactoe ├── .gitignore ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── index.css │ └── index.js └── yarn.lock ├── todo-pwa ├── .gitignore ├── README.md ├── package.json ├── public │ ├── assets │ │ ├── icon-192.png │ │ └── icon-512.png │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── actions │ │ └── index.js │ ├── components │ │ ├── AddTodo.js │ │ ├── App.js │ │ ├── Filter.js │ │ ├── Todo.js │ │ └── TodoList.js │ ├── constants │ │ ├── action-types.js │ │ └── filters.js │ ├── index.js │ ├── reducers │ │ ├── filter.js │ │ ├── index.js │ │ └── todos.js │ └── registerServiceWorker.js └── yarn.lock ├── todo ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── index.css │ └── index.js └── yarn.lock └── trip-mate ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── assets │ └── bg.jpg ├── components │ ├── App.js │ ├── Error404.js │ ├── Nav.js │ └── trips │ │ ├── AddTrip.js │ │ ├── Trip.js │ │ ├── TripsCount.js │ │ └── TripsList.js ├── index.js └── stylesheets │ ├── index.css │ └── index.scss └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | .vscode 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Projects 2 | 3 | (not maintained, was just for practice earlier) 4 | A couple of projects to get familiar with the React framework. 5 | 6 | ![Giphy](https://media.giphy.com/media/l0HUhhAtb86vYAHkY/giphy.gif) 7 | ![Giphy](https://media.giphy.com/media/26gN147Cy7zF1Mv7y/giphy.gif) 8 | ![Giphy](https://media.giphy.com/media/26DNeo2xDmfj3plbW/giphy.gif) 9 | 10 | 11 | ## Projects 12 | 1. **todo**: Basic todo app to get started. 13 | 2. **tictactoe**: A tic-tac-toe game uses states and props. 14 | 3. **markdown-editor**: A real-time markdown editor SPA. [(Live Demo)](https://react-markdown-editor.firebaseapp.com) 15 | 4. **basic-react-redux**: Basic react-redux project which handles articles in its store. 16 | 5. **basic-redux-thunk**: Basic react-redux-thunk project which handles items from an API using thunk, uses redux for store. 17 | 6. **book-store [WIP]**: Online book store built over react-redux and Bootstrap for UI. 18 | 7. **trip-mate**: Manage and keep track of your trips with this simple, smooth webapp. Uses react-router, custom styling. [(Live Demo)](https://react-trip-mate.firebaseapp.com) 19 | 8. **property-finder**: Search for and list properties. Built on React-Native, uses react-navigation. 20 | 9. **todo-pwa**: The classic Todos Tracker as a Progressive Web App. Built using React, Redux, leveraging styled-components, PaperCSS, Service Worker tools. Data is retained using localStorage [(Live Demo)](https://paper-todo.firebaseapp.com) 21 | 10. **gatsbyjs-markdown-blog**: GatsbyJS blog which fetches markdown blog posts, lists them and displays each post wrapped up as a static site. Based on the tutorials in the official docs, uses GraphQL, Typography.js, Remark plugin, Glamor. 22 | 11. **reddit-clone**: react-redux-thunk project that calls the RedditAPI to fetch posts from selected subreddits. [(Live Demo)](https://github.com/ajayns/frontend-dev-talk) 23 | 24 | 25 | ## Development 26 | 27 | Projects such as _create-react-app_, _nwb_ and _react-slingshot_ have been used to setup development envs. 28 | 29 | Install all the required packages/dependencies using yarn 30 | 31 | ### `yarn` 32 | 33 | Serve the app to browser 34 | 35 | ### `yarn start` 36 | 37 | 38 | ## Tutorials/References 39 | 40 | https://reactjs.org/tutorial/tutorial.html 41 | 42 | https://egghead.io/courses/the-beginner-s-guide-to-reactjs 43 | 44 | https://www.valentinog.com/blog/react-redux-tutorial-beginners/ 45 | 46 | https://medium.com/@stowball/a-dummys-guide-to-redux-and-thunk-in-react-d8904a7005d3 47 | 48 | https://www.raywenderlich.com/178012/react-native-tutorial-building-android-apps-javascript 49 | 50 | https://medium.com/@rajaraodv/step-by-step-guide-to-building-react-redux-apps-using-mocks-48ca0f47f9a 51 | 52 | https://www.gatsbyjs.org/tutorial/part-four/ 53 | 54 | https://redux.js.org/advanced/example-reddit-api 55 | 56 | ## Contributing 57 | 58 | Sorry, as this was a practice repo and no longer maintained, will not be updating the repo nor looking into issues/PRs. 59 | -------------------------------------------------------------------------------- /basic-react-redux/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react"], 3 | "plugins": [ 4 | "transform-object-rest-spread" 5 | ] 6 | } -------------------------------------------------------------------------------- /basic-react-redux/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Valentino Gagliardi 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 | -------------------------------------------------------------------------------- /basic-react-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minimal-react-webpack", 3 | "version": "1.0.0", 4 | "description": "A minimal React Webpack project to get you started", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack-dev-server --open" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/valentinogagliardi/minimal-react-webpack.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/valentinogagliardi/minimal-react-webpack/issues" 19 | }, 20 | "homepage": "https://github.com/valentinogagliardi/minimal-react-webpack#readme", 21 | "devDependencies": { 22 | "babel-core": "^6.26.0", 23 | "babel-loader": "^7.1.2", 24 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 25 | "babel-preset-env": "^1.6.1", 26 | "babel-preset-react": "^6.24.1", 27 | "html-loader": "^0.5.1", 28 | "html-webpack-plugin": "^2.30.1", 29 | "prop-types": "^15.6.0", 30 | "react": "^16.2.0", 31 | "react-dom": "^16.2.0", 32 | "react-redux": "^5.0.6", 33 | "redux": "^3.7.2", 34 | "uuid": "^3.2.1", 35 | "webpack": "^3.10.0", 36 | "webpack-dev-server": "^2.9.7" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /basic-react-redux/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | basic react-redux 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /basic-react-redux/src/js/actions/index.js: -------------------------------------------------------------------------------- 1 | import { ADD_ARTICLE } from "../constants/action-types"; 2 | 3 | // Action to add article to store 4 | export const addArticle = article => ({ 5 | type: ADD_ARTICLE, 6 | payload: article 7 | }); -------------------------------------------------------------------------------- /basic-react-redux/src/js/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import List from "./List"; 3 | import Form from "./Form"; 4 | 5 | // Root structure 6 | const App = () => ( 7 |
8 |
9 |

Articles

10 | 11 |
12 |
13 |

Add a new article

14 |
15 |
16 |
17 | ); 18 | 19 | export default App; -------------------------------------------------------------------------------- /basic-react-redux/src/js/components/Form.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import uuidv1 from "uuid"; 4 | import PropTypes from "prop-types"; 5 | import { addArticle } from "../actions/index"; 6 | 7 | // Dispatch action to add to store 8 | const mapDispatchToProps = dispatch => { 9 | return { 10 | addArticle: article => dispatch(addArticle(article)) 11 | }; 12 | }; 13 | 14 | // Form component 15 | class ConnectedForm extends Component { 16 | constructor() { 17 | super(); 18 | 19 | this.state = { 20 | title: "", 21 | } 22 | 23 | this.handleChange = this.handleChange.bind(this); 24 | this.handleSubmit = this.handleSubmit.bind(this); 25 | } 26 | 27 | handleChange(e) { 28 | this.setState({ 29 | title: e.target.value 30 | }) 31 | } 32 | 33 | handleSubmit(e) { 34 | e.preventDefault(); 35 | const { title } = this.state; 36 | const id = uuidv1(); 37 | this.props.addArticle({ 38 | title, 39 | id 40 | }); 41 | this.setState({ 42 | title: "" 43 | }) 44 | } 45 | 46 | render() { 47 | const { title } = this.state; 48 | return ( 49 | 50 |
51 | 52 | 59 |
60 | 63 | 64 | ); 65 | } 66 | } 67 | 68 | // Wire it up together and export 69 | const Form = connect(null, mapDispatchToProps)(ConnectedForm); 70 | 71 | // Check var types using propTypes 72 | ConnectedForm.propTypes = { 73 | addArticle: PropTypes.func.isRequired 74 | }; 75 | 76 | export default Form; -------------------------------------------------------------------------------- /basic-react-redux/src/js/components/List.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import PropTypes from "prop-types"; 4 | 5 | // Get state from store to props 6 | const mapStateToProps = state => { 7 | return { articles: state.articles}; 8 | }; 9 | 10 | const ConnectedList = ({articles}) => ( 11 | 18 | ); 19 | 20 | // Wire it up together, check types and export 21 | const List = connect(mapStateToProps)(ConnectedList); 22 | 23 | ConnectedList.propTypes = { 24 | articles: PropTypes.array.isRequired 25 | }; 26 | 27 | export default List; -------------------------------------------------------------------------------- /basic-react-redux/src/js/constants/action-types.js: -------------------------------------------------------------------------------- 1 | // Constants used instead of strings to prevent typo issues 2 | 3 | export const ADD_ARTICLE = "ADD_ARTICLE"; -------------------------------------------------------------------------------- /basic-react-redux/src/js/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | import { Provider } from "react-redux"; 4 | 5 | import store from "../js/store/index"; 6 | import App from "../js/components/App"; 7 | 8 | // Provider to init redux store with app 9 | render( 10 | 11 | 12 | , 13 | document.getElementById("app") 14 | ); -------------------------------------------------------------------------------- /basic-react-redux/src/js/reducers/articleReducer.js: -------------------------------------------------------------------------------- 1 | import { ADD_ARTICLE } from "../constants/action-types"; 2 | 3 | // Reducer to add article 4 | const articleReducer = (state = [], action) => { 5 | switch (action.type) { 6 | case ADD_ARTICLE: 7 | return [...state, action.payload]; 8 | default: 9 | return state; 10 | } 11 | } 12 | 13 | export default articleReducer; -------------------------------------------------------------------------------- /basic-react-redux/src/js/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import articleReducer from "./articleReducer"; 3 | 4 | // Combine all reducers as root reducer 5 | export default combineReducers({articles: articleReducer}); -------------------------------------------------------------------------------- /basic-react-redux/src/js/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from "redux"; 2 | import rootReducer from "../reducers/index"; 3 | 4 | // Configure store with reducers and create 5 | const store = createStore(rootReducer); 6 | 7 | export default store; 8 | -------------------------------------------------------------------------------- /basic-react-redux/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebPackPlugin = require("html-webpack-plugin"); 3 | 4 | module.exports = { 5 | entry: ["./src/js/index.js"], 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "js/[name].js" 9 | }, 10 | devServer: { 11 | contentBase: "./dist" 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.js$/, 17 | exclude: /node_modules/, 18 | use: { 19 | loader: "babel-loader" 20 | } 21 | }, 22 | { 23 | test: /\.html$/, 24 | use: [ 25 | { 26 | loader: "html-loader" 27 | } 28 | ] 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new HtmlWebPackPlugin({ 34 | template: "./src/index.html", 35 | filename: "./index.html" 36 | }) 37 | ] 38 | }; 39 | -------------------------------------------------------------------------------- /basic-redux-thunk/nwb.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'react-app' 3 | } 4 | -------------------------------------------------------------------------------- /basic-redux-thunk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dummys-guide-to-redux-and-thunk-react", 3 | "version": "1.0.0", 4 | "description": "A Dummy's Guide to Redux and Thunk in React", 5 | "private": true, 6 | "scripts": { 7 | "build": "nwb build-react-app", 8 | "clean": "nwb clean-app", 9 | "start": "nwb serve-react-app" 10 | }, 11 | "dependencies": { 12 | "react": "^15.3.2", 13 | "react-dom": "^15.3.2", 14 | "react-redux": "^4.4.5", 15 | "redux": "^3.6.0", 16 | "redux-thunk": "^2.1.0" 17 | }, 18 | "devDependencies": { 19 | "babel-eslint": "^7.1.0", 20 | "eslint": "^3.10.0", 21 | "eslint-plugin-react": "^6.6.0", 22 | "nwb": "0.12.x" 23 | }, 24 | "author": "", 25 | "license": "MIT", 26 | "repository": "" 27 | } 28 | -------------------------------------------------------------------------------- /basic-redux-thunk/src/actions/items.js: -------------------------------------------------------------------------------- 1 | // Helper actions for error, loading and data fetch success 2 | 3 | export const itemsHasErrored = (bool) => { 4 | return { 5 | type: 'ITEMS_HAS_ERRORED', 6 | hasErrored: bool 7 | }; 8 | } 9 | 10 | export const itemsIsLoading = (bool) => { 11 | return { 12 | type: 'ITEMS_IS_LOADING', 13 | isLoading: bool 14 | }; 15 | } 16 | 17 | export const itemsFetchDataSuccess = (items) => { 18 | return { 19 | type: 'ITEMS_FETCH_DATA_SUCCESS', 20 | items 21 | }; 22 | } 23 | 24 | // Driver action to fetch data 25 | 26 | export const itemsFetchData = (url) => { 27 | return (dispatch) => { 28 | dispatch(itemsIsLoading(true)); 29 | 30 | fetch(url) 31 | .then((res) => { 32 | if (!res.ok) { 33 | throw Error(res.statusText); 34 | } 35 | 36 | dispatch(itemsIsLoading(false)); 37 | 38 | return res; 39 | }) 40 | .then((res) => res.json()) 41 | .then((items) => dispatch(itemsFetchDataSuccess(items))) 42 | .catch(() => dispatch(itemsHasErrored(true))); 43 | } 44 | } -------------------------------------------------------------------------------- /basic-redux-thunk/src/components/ItemList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { itemsFetchData } from '../actions/items'; 4 | 5 | class ItemList extends Component { 6 | componentDidMount() { 7 | this.props.fetchData('http://599167402df2f40011e4929a.mockapi.io/items') 8 | } 9 | 10 | render() { 11 | if (this.props.hasErrored) { 12 | return

There has been an Error

; 13 | } 14 | 15 | if (this.props.isLoading) { 16 | return

Loading...

; 17 | } 18 | 19 | return ( 20 | 27 | ); 28 | } 29 | } 30 | 31 | // Get state data from store to props 32 | const mapStateToProps = (state) => { 33 | return { 34 | items: state.items, 35 | hasErrored: state.itemsHasErrored, 36 | isLoading: state.itemsIsLoading 37 | }; 38 | } 39 | 40 | // Get actions to handle store data 41 | const mapDispatchToProps = (dispatch) => { 42 | return { 43 | fetchData: (url) => dispatch(itemsFetchData(url)) 44 | }; 45 | } 46 | 47 | // Wire it all up and export 48 | export default connect(mapStateToProps, mapDispatchToProps)(ItemList); 49 | -------------------------------------------------------------------------------- /basic-redux-thunk/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Basic Redux Thunk 8 | 13 | 14 | 15 | 16 |

React Redux Thunk

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /basic-redux-thunk/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import { Provider } from 'react-redux'; 5 | import configureStore from './store/configureStore'; 6 | 7 | import ItemList from './components/ItemList'; 8 | 9 | // Init store 10 | const store = configureStore(); 11 | 12 | // Provider to use redux store in app 13 | render( 14 | 15 | 16 | , 17 | document.getElementById('app') 18 | ); -------------------------------------------------------------------------------- /basic-redux-thunk/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { items, itemsHasErrored, itemsIsLoading } from './items'; 3 | 4 | // Combine all reducers into root reducer 5 | export default combineReducers({ 6 | items, 7 | itemsHasErrored, 8 | itemsIsLoading 9 | }); -------------------------------------------------------------------------------- /basic-redux-thunk/src/reducers/items.js: -------------------------------------------------------------------------------- 1 | // Reducers for error, loading and data fetch 2 | 3 | export const itemsHasErrored = (state = false, action) => { 4 | switch (action.type) { 5 | case 'ITEMS_HAS_ERRORED': 6 | return action.hasErrored; 7 | 8 | default: 9 | return state; 10 | } 11 | } 12 | 13 | export const itemsIsLoading = (state = false, action) => { 14 | switch (action.type) { 15 | case 'ITEMS_IS_LOADING': 16 | return action.isLoading; 17 | 18 | default: 19 | return state; 20 | } 21 | } 22 | 23 | export const items = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ITEMS_FETCH_DATA_SUCCESS': 26 | return action.items; 27 | 28 | default: 29 | return state; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /basic-redux-thunk/src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import rootReducer from '../reducers'; 4 | 5 | // Configure the store with reducers, initial state (if provided) and react-thunk 6 | export default function configureStore(initialState) { 7 | return createStore( 8 | rootReducer, 9 | initialState, 10 | applyMiddleware(thunk) 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /book-store/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest", 4 | "react", 5 | "stage-1" 6 | ], 7 | "env": { 8 | "development": { 9 | "presets": [ 10 | "react-hmre" 11 | ] 12 | }, 13 | "production": { 14 | "plugins": [ 15 | "transform-react-constant-elements", 16 | "transform-react-remove-prop-types" 17 | ] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /book-store/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /book-store/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:import/errors", 6 | "plugin:import/warnings" 7 | ], 8 | "plugins": [ 9 | "react" 10 | ], 11 | "parser": "babel-eslint", 12 | "parserOptions": { 13 | "ecmaVersion": 6, 14 | "sourceType": "module", 15 | "ecmaFeatures": { 16 | "jsx": true, 17 | "experimentalObjectRestSpread": true 18 | } 19 | }, 20 | "env": { 21 | "es6": true, 22 | "browser": true, 23 | "node": true, 24 | "jquery": true, 25 | "mocha": true 26 | }, 27 | "rules": { 28 | "quotes": 0, 29 | "no-console": 1, 30 | "no-debugger": 1, 31 | "no-var": 1, 32 | "semi": [1, "always"], 33 | "no-trailing-spaces": 0, 34 | "eol-last": 0, 35 | "no-underscore-dangle": 0, 36 | "no-alert": 0, 37 | "no-lone-blocks": 0, 38 | "jsx-quotes": 1, 39 | "react/display-name": [ 1, {"ignoreTranspilerName": false }], 40 | "react/forbid-prop-types": [1, {"forbid": ["any"]}], 41 | "react/jsx-boolean-value": 0, 42 | "react/jsx-closing-bracket-location": 0, 43 | "react/jsx-curly-spacing": 1, 44 | "react/jsx-indent-props": 0, 45 | "react/jsx-key": 1, 46 | "react/jsx-max-props-per-line": 0, 47 | "react/jsx-no-bind": 0, 48 | "react/jsx-no-duplicate-props": 1, 49 | "react/jsx-no-literals": 0, 50 | "react/jsx-no-undef": 1, 51 | "react/jsx-pascal-case": 1, 52 | "react/jsx-sort-prop-types": 0, 53 | "react/jsx-sort-props": 0, 54 | "react/jsx-uses-react": 1, 55 | "react/jsx-uses-vars": 1, 56 | "react/no-danger": 1, 57 | "react/no-did-mount-set-state": 1, 58 | "react/no-did-update-set-state": 1, 59 | "react/no-direct-mutation-state": 1, 60 | "react/no-multi-comp": 1, 61 | "react/no-set-state": 0, 62 | "react/no-unknown-property": 1, 63 | "react/prefer-es6-class": 1, 64 | "react/prop-types": 1, 65 | "react/react-in-jsx-scope": 1, 66 | "react/require-extension": 1, 67 | "react/self-closing-comp": 1, 68 | "react/sort-comp": 1, 69 | "react/wrap-multilines": 1 70 | }, 71 | "globals": { 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /book-store/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Node version: 2 | 3 | npm version: 4 | 5 | Operating system: 6 | 7 | Command line used: 8 | 9 | Steps to reproduce: 10 | -------------------------------------------------------------------------------- /book-store/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log* 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | #dist folder 30 | dist 31 | 32 | # IDEA/Webstorm project files 33 | .idea 34 | *.iml 35 | 36 | #VSCode metadata 37 | .vscode 38 | 39 | # Mac files 40 | .DS_Store 41 | -------------------------------------------------------------------------------- /book-store/.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | excludes: ['*.spec.js'] 3 | extensions: ['.js'] 4 | -------------------------------------------------------------------------------- /book-store/.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /book-store/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | before_install: 3 | # Repo for Yarn 4 | - sudo apt-key adv --keyserver pgp.mit.edu --recv D101F7899D41F3C3 5 | - echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 6 | - sudo apt-get update -qq 7 | - sudo apt-get install -y -qq yarn 8 | install: 9 | - yarn install 10 | cache: 11 | directories: 12 | - $HOME/.yarn-cache 13 | node_js: 14 | - "6" 15 | - "5" 16 | - "4" 17 | after_success: 18 | # Send coverage data to coveralls. 19 | - npm run test:cover:travis 20 | -------------------------------------------------------------------------------- /book-store/appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against this version of Node.js 2 | environment: 3 | matrix: 4 | # node.js 5 | - nodejs_version: "6" 6 | - nodejs_version: "5" 7 | - nodejs_version: "4" 8 | 9 | # Install scripts. (runs after repo cloning) 10 | install: 11 | # Get the latest stable version of Node.js or io.js 12 | - ps: Install-Product node $env:nodejs_version 13 | # install modules 14 | - yarn 15 | 16 | cache: 17 | - "%LOCALAPPDATA%/Yarn" 18 | 19 | # Post-install test scripts. 20 | test_script: 21 | # Output useful info for debugging. 22 | - node --version 23 | - npm --version 24 | # run tests 25 | - npm test 26 | 27 | # Don't actually build. 28 | build: off 29 | 30 | notifications: 31 | - provider: Email 32 | to: 33 | - housecor@gmail.com 34 | subject: 'Build failed: react-slingshot' 35 | message: The continuous integration build failed. See https://ci.appveyor.com/project/CoryHouse/react-slingshot/ for details. 36 | on_build_success: false 37 | on_build_failure: true 38 | on_build_status_changed: false 39 | -------------------------------------------------------------------------------- /book-store/docs/FAQ.md: -------------------------------------------------------------------------------- 1 | ##FAQ 2 | ###Why does this exist? 3 | This starter kit implements best practices like testing, minification, bundling, and so on. It codifies a long list of decisions that you no longer have to make to get rolling. It saves you from the long, painful process of wiring it all together into an automated dev environment and build process. It's also useful as inspiration for ideas you might want to integrate into your current development environment or build process. 4 | 5 | ###What do the scripts in package.json do? 6 | Unfortunately, scripts in package.json can't be commented inline because the JSON spec doesn't support comments, so I'm providing info on what each script in package.json does here. 7 | 8 | | **Script** | **Description** | 9 | |----------|-------| 10 | | remove-demo | Removes the demo application so you can begin development. | 11 | | prestart | Runs automatically before start. Calls remove-dist script which deletes the dist folder. This helps remind you to run the build script before committing since the dist folder will be deleted if you don't. ;) | 12 | | start | Runs tests, lints, starts dev webserver, and opens the app in your default browser. | 13 | | lint:tools | Runs ESLint on build related JS files. (eslint-loader lints src files via webpack when `npm start` is run) | 14 | | clean-dist | Removes everything from the dist folder. | 15 | | remove-dist | Deletes the dist folder. | 16 | | create-dist | Creates the dist folder and the necessary subfolders. | 17 | | prebuild | Runs automatically before build script (due to naming convention). Cleans dist folder, builds html, and builds sass. | 18 | | build | Bundles all JavaScript using webpack and writes it to /dist. | 19 | | test | Runs tests (files ending in .spec.js) using Mocha and outputs results to the command line. Watches all files so tests are re-run upon save. | 20 | | test:cover | Runs tests as described above. Generates a HTML coverage report to ./coverage/index.html | 21 | | test:cover:travis | Runs coverage as described above, however sends machine readable lcov data to Coveralls. This should only be used from the travis build! | 22 | 23 | ### Can you explain the folder structure? 24 | ``` 25 | . 26 | ├── .babelrc # Configures Babel 27 | ├── .editorconfig # Configures editor rules 28 | ├── .eslintrc # Configures ESLint 29 | ├── .gitignore # Tells git which files to ignore 30 | ├── .istanbul.yml # Configure istanbul code coverage 31 | ├── .npmrc # Configures npm to save exact by default 32 | ├── README.md # This file. 33 | ├── dist # Folder where the build script places the built app. Use this in prod. 34 | ├── package.json # Package configuration. The list of 3rd party libraries and utilities 35 | ├── src # Source code 36 | │   ├── actions # Flux/Redux actions. List of distinct actions that can occur in the app. 37 | │   ├── components # React components 38 | │   ├── constants # Application constants including constants for Redux 39 | │   ├── containers # Top-level React components that interact with Redux 40 | │   ├── favicon.ico # favicon to keep your browser from throwing a 404 during dev. Not actually used in prod build. 41 | │   ├── index.ejs # Template for homepage 42 | │   ├── index.js # Entry point for your app 43 | │   ├── reducers # Redux reducers. Your state is altered here based on actions 44 | │   ├── store # Redux store configuration 45 | │   ├── styles # CSS Styles, typically written in Sass 46 | │   └── utils # Plain old JS objects (POJOs). Pure logic. No framework specific code here. 47 | ├── tools # Node scripts that run build related tools 48 | │   ├── setup # **NEEDS DOCUMENTATION** 49 | │ │   ├── setup.js # **NEEDS DOCUMENTATION** 50 | │ │   ├── setupMessage.js # **NEEDS DOCUMENTATION** 51 | │ │   └── setupPrompts.js # **NEEDS DOCUMENTATION** 52 | │   ├── build.js # Runs the production build 53 | │   ├── chalkConfig.js # Centralized configuration for chalk (adds color to console statements) 54 | │   ├── distServer.js # Starts webserver and opens final built app that's in dist in your default browser 55 | │   ├── nodeVersionCheck.js # **NEEDS DOCUMENTATION** 56 | │   ├── removeDemo.js # **NEEDS DOCUMENTATION** 57 | │   ├── srcServer.js # Starts dev webserver with hot reloading and opens your app in your default browser 58 | │   ├── startMessage.js # **NEEDS DOCUMENTATION** 59 | │   └── testSetup.js # Configures mocha 60 | ├── webpack.config.dev.js # Configures webpack for development builds 61 | └── webpack.config.prod.js # Configures webpack for production builds 62 | ``` 63 | 64 | ### What are the dependencies in package.json used for? 65 | | **Dependency** | **Use** | 66 | |----------|-------| 67 | |autoprefixer | Automatically adds vendor prefixes, using data from Can I Use. | 68 | |connect-history-api-fallback | Support reloading deep links | 69 | |object-assign | Polyfill for Object.assign | 70 | |react|React library | 71 | |react-dom|React library for DOM rendering | 72 | |react-redux|Redux library for connecting React components to Redux | 73 | |react-router|React library for routing | 74 | |redux|Library for unidirectional data flows | 75 | |redux-thunk|Middleware for redux that allows actions to be declared as functions | 76 | |babel-cli|Babel Command line interface | 77 | |babel-core|Babel Core for transpiling the new JavaScript to old | 78 | |babel-loader|Adds Babel support to Webpack | 79 | |babel-plugin-react-display-name| Add displayName to React.createClass calls | 80 | |babel-plugin-transform-react-constant-elements | Performance optimization: Hoists the creation of elements that are fully static to the top level. reduces calls to React.createElement and the resulting memory allocations. [More info](https://medium.com/doctolib-engineering/improve-react-performance-with-babel-16f1becfaa25#.2wbkg8g4d) | 81 | |babel-preset-latest|Babel preset for ES2015, ES2016 and ES2017| 82 | |babel-preset-react-hmre|Hot reloading preset for Babel| 83 | |babel-preset-react| Add JSX support to Babel | 84 | |babel-preset-stage-1| Include stage 1 feature support in Babel | 85 | |browser-sync| Supports synchronized testing on multiple devices and serves local app on public URL | 86 | |chai|Assertion library for use with Mocha| 87 | |chalk|Adds color support to terminal | 88 | |cross-env|Cross-environment friendly way to handle environment variables| 89 | |css-loader|Add CSS support to Webpack| 90 | |enzyme|Simplified JavaScript Testing utilities for React| 91 | |eslint|Lints JavaScript | 92 | |eslint-loader|Adds ESLint support to Webpack | 93 | |eslint-plugin-react|Adds additional React-related rules to ESLint| 94 | |eslint-watch|Wraps ESLint to provide file watch support and enhanced command line output| 95 | |extract-text-webpack-plugin| Extracts CSS into separate file for production build | 96 | |file-loader| Adds file loading support to Webpack | 97 | |html-webpack-plugin|Generates custom index.html for each environment as part of webpack build| 98 | |mocha| JavaScript testing library | 99 | |node-sass| Adds SASS support to Webpack | 100 | |npm-run-all| Run multiple scripts at the same time | 101 | |postcss-loader| Adds PostCSS support to Webpack | 102 | |react-addons-test-utils| Adds React TestUtils | 103 | |rimraf|Delete files | 104 | |sass-loader| Adds Sass support to Webpack| 105 | |sinon| Standalone test spies, stubs and mocks for JavaScript | 106 | |sinon-chai| Extends Chai with assertions for the Sinon.JS mocking framework| 107 | |style-loader| Add Style support to Webpack | 108 | |webpack| Bundler with plugin system and integrated development server | 109 | |webpack-dev-middleware| Used to integrate Webpack with Browser-sync | 110 | |webpack-hot-middleware| Use to integrate Webpack's hot reloading support with Browser-sync | 111 | |webpack-md5-hash| Hash bundles, and use the hash for the filename so that the filename only changes when contents change| 112 | |yargs| Easily parse command-line arguments | 113 | 114 | ### Where are the files being served from when I run `npm start`? 115 | Webpack serves your app in memory when you run `npm start`. No physical files are written. However, the web root is /src, so you can reference files under /src in index.html. When the app is built using `npm run build`, physical files are written to /dist and the app is served from /dist. 116 | 117 | ### Where is index.html? 118 | It's generated by webpack using htmlWebpackPlugin. This plugin dynamically generates index.html based on the configuration in webpack.config. It also adds references to the JS and CSS bundles using hash-based filenames to bust cache. Separate bundles for vendor and application code are created and referencing within the generated index.html file so that vendor libraries and app code can be cached separately by the browser. The bundle filenames are based on the file's hash, so the filenames only change when the file contents change. For more information on this, read [Long-term caching of static assets with Webpack](https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.4aeatmtfz) and [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) 119 | 120 | ### How is Sass being converted into CSS and landing in the browser? 121 | Magic! Okay, more specifically, we're handling it differently in dev (`npm start`) vs prod (`npm run build`) 122 | 123 | When you run `npm start`: 124 | 125 | 1. The sass-loader compiles Sass into CSS 126 | 2. Webpack bundles the compiled CSS into bundle.js. Sounds odd, but it works! 127 | 3. bundle.js contains code that loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either. 128 | 129 | The approach above supports hot reloading, which is great for development. However, it also create a flash of unstyled content on load because you have to wait for the JavaScript to parse and load styles before they're applied. So for the production build, we use a different approach: 130 | 131 | When you run `npm run build`: 132 | 133 | 1. The sass-loader compiles Sass into CSS 134 | 2. The [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) extracts the compiled Sass into styles.css 135 | 3. buildHtml.js adds a reference to the stylesheet to the head of index.html. 136 | 137 | For both of the above methods, a separate sourcemap is generated for debugging Sass in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass). 138 | 139 | ### I don't like the magic you just described above. I simply want to use a CSS file. 140 | No problem. Reference your CSS file in index.html, and add a step to the build process to copy your CSS file over to the same relative location /dist as part of the build step. But be forwarned, you lose style hot reloading with this approach. 141 | 142 | ### I just want an empty starter kit. 143 | This starter kit includes an example app so you can see how everything hangs together on a real app. When you're done reviewing it, run this to remove the demo app: 144 | 145 | `npm run remove-demo` 146 | 147 | Don't want to use Redux? See the next question for some steps on removing Redux. 148 | 149 | ### Do I have to use Redux? 150 | Nope. Redux is useful for applications with more complex data flows. If your app is simple, Redux is overkill. Remove Redux like this: 151 | 152 | 1. Run `npm run remove-demo` 153 | 2. Uninstall Redux related packages: `npm uninstall redux react-redux redux-thunk` 154 | 3. Create a new empty component in /components. 155 | 4. Call render on the new top level component you created in step 3 in src/index.js. 156 | 157 | ### How do I remove React Router? 158 | 1. Uninstall React Router and routing related packages: `npm uninstall --save react-router` 159 | 2. Delete the following files: `src/routes.js` 160 | 3. Remove `import { Link, IndexLink } from 'react-router';` from top of `src/components/App.js`, add a reference to `src/components/FuelSavingsForm.js`, and replace body of (implicit) render with this: ``. 161 | 162 | ### How do I deploy this? 163 | `npm run build`. This will build the project for production. It does the following: 164 | * Minifies all JS 165 | * Sets NODE_ENV to prod so that React is built in production mode 166 | * Places the resulting built project files into /dist. (This is the folder you'll expose to the world). 167 | 168 | ### Why are test files placed alongside the file under test (instead of centralized)? 169 | Streamlined automated testing is a core feature of this starter kit. All tests are placed in files that end in .spec.js. Spec files are placed in the same directory as the file under test. Why? 170 | + The existence of tests is highly visible. If a corresponding .spec file hasn't been created, it's obvious. 171 | + Easy to open since they're in the same folder as the file being tested. 172 | + Easy to create new test files when creating new source files. 173 | + Short import paths are easy to type and less brittle. 174 | + As files are moved, it's easy to move tests alongside. 175 | 176 | That said, you can of course place your tests under /test instead, which is the Mocha default. If you do, you can simplify the test script to no longer specify the path. Then Mocha will simply look in /test to find your spec files. 177 | 178 | ### How do I debug? 179 | Since browsers don't currently support ES6, we're using Babel to compile our ES6 down to ES5. This means the code that runs in the browser looks different than what we wrote. But good news, a [sourcemap](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) is generated to enable easy debugging. This means your original JS source will be displayed in your browser's dev console. 180 | *Note:* When you run `npm start`, no JS is minified. Why? Because minifying slows the build. So JS is only minified when you run the `npm run build` script. See [more on building for production below](https://github.com/coryhouse/react-slingshot#how-do-i-deploy-this). 181 | 182 | Also note that no actual physical files are written to the filesystem during the dev build. **For performance, all files exist in memory when served from the webpack server.**. Physical files are only written when you run `npm run build`. 183 | 184 | **Tips for debugging via sourcemaps:** 185 | 186 | 1. Browsers vary in the way they allow you to view the original source. Chrome automatically shows the original source if a sourcemap is available. Safari, in contrast, will display the minified source and you'll [have to cmd+click on a given line to be taken to the original source](http://stackoverflow.com/questions/19550060/how-do-i-toggle-source-mapping-in-safari-7). 187 | 2. Do **not** enable serving files from your filesystem in Chrome dev tools. If you do, Chrome (and perhaps other browsers) may not show you the latest version of your code after you make a source code change. Instead **you must close the source view tab you were using and reopen it to see the updated source code**. It appears Chrome clings to the old sourcemap until you close and reopen the source view tab. To clarify, you don't have to close the actual tab that is displaying the app, just the tab in the console that's displaying the source file that you just changed. 188 | 3. If the latest source isn't displaying the console, force a refresh. Sometimes Chrome seems to hold onto a previous version of the sourcemap which will cause you to see stale code. 189 | 190 | #### Debugging in Visual Studio Code: 191 | * Install the [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) extension. 192 | * Follow the instructions on how to [configure debugging in Visual Studio code](https://github.com/Microsoft/vscode-chrome-debug/blob/master/README.md#using-the-debugger). 193 | 194 | Don't see your favorite code editor debugging configuration here? Submit a PR and we'll be glad to add it to the FAQ.md. 195 | 196 | ### Why does the build use npm scripts instead of Gulp or Grunt? 197 | In short, Gulp is an unnecessary abstraction that creates more problems than it solves. [Here's why](https://medium.com/@housecor/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.vtaziro8n). 198 | 199 | ### Why does package.json reference the exact version? 200 | This assures that the build won't break when some new version is released. Unfortunately, many package authors don't properly honor [Semantic Versioning](http://semver.org), so instead, as new versions are released, I'll test them and then introduce them into the starter kit. But yes, this means when you do `npm update` no new dependencies will be pulled down. You'll have to update package.json with the new version manually. 201 | 202 | ### How do I handle images? 203 | Via Webpack's file loader. Example: 204 | 205 | ``` 206 | 207 | 208 | ``` 209 | 210 | Webpack will then intelligently handle your image for you. For the production build, it will copy the physical file to /dist, give it a unique filename, and insert the appropriate path in your image tag. 211 | 212 | ### I'm getting an error when running npm install: Failed to locate "CL.exe" 213 | On Windows, you need to install extra dependencies for browser-sync to build and install successfully. Follow the getting started steps above to assure you have the necessary dependencies on your machine. 214 | 215 | ### I can't access the external URL for Browsersync 216 | To hit the external URL, all devices must be on the same LAN. So this may mean your dev machine needs to be on the same Wifi as the mobile devices you're testing. 217 | 218 | ### What about the Redux Devtools? 219 | Install the [Redux devtools extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en) in Chrome Developer Tools. If you're interested in running Redux dev tools cross-browser, Barry Staes created a [branch with the devtools incorporated](https://github.com/coryhouse/react-slingshot/pull/27). 220 | 221 | ### Hot reloading isn't working! 222 | Hot reloading doesn't always play nicely with stateless functional components at this time. [This is a known limitation that is currently being worked](https://github.com/gaearon/babel-plugin-react-transform/issues/57). To avoid issues with hot reloading for now, use a traditional class-based React component at the top of your component hierarchy. 223 | 224 | ### How do I setup code coverage reporting? 225 | Using the `npm run test:cover` command to run the tests, building a code coverage report. The report is written to `coverage/index.html`. Slingshot provides a script for this: 226 | 227 | ```bash 228 | npm run open:cover 229 | ``` 230 | 231 | You can add code coverage metrics to your `README.md` file and pull by integrating with [Coveralls](https://coveralls.io/). 232 | 233 | 1. Sign in to Coveralls with your GitHub account. 234 | 2. Authorise Coveralls to access your repositories. 235 | 3. Choose 'Add Repo' and select your repo. 236 | 237 | That's it! Travis will now execute the `npm run test:cover:travis` script after a successful build, which will write the coverage report in the standard lcov format and send it directly to Coveralls. The environment variables provided for travis jobs are used to automatically target the correct Coveralls project, as long as it is set up as described above. 238 | 239 | You can get the badge from the Coveralls website. 240 | 241 | ###What about TypeScript? 242 | Here's a [fork with TS support](https://github.com/typescriptcrew/ts-react-slingshot): 243 | 244 | -------------------------------------------------------------------------------- /book-store/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "book-store", 3 | "version": "0.1.0", 4 | "description": "A basic online book store built over react-redux", 5 | "engines": { 6 | "npm": ">=3" 7 | }, 8 | "scripts": { 9 | "preinstall": "node tools/nodeVersionCheck.js", 10 | "setup": "node tools/setup/setupMessage.js && npm install && node tools/setup/setup.js", 11 | "remove-demo": "babel-node tools/removeDemo.js", 12 | "start-message": "babel-node tools/startMessage.js", 13 | "prestart": "npm-run-all --parallel start-message remove-dist", 14 | "start": "npm-run-all --parallel test:watch open:src lint:watch", 15 | "open:src": "babel-node tools/srcServer.js", 16 | "open:dist": "babel-node tools/distServer.js", 17 | "lint": "esw webpack.config.* src tools --color", 18 | "lint:watch": "npm run lint -- --watch", 19 | "clean-dist": "npm run remove-dist && mkdir dist", 20 | "remove-dist": "rimraf ./dist", 21 | "prebuild": "npm run clean-dist && npm run lint && npm run test", 22 | "build": "babel-node tools/build.js && npm run open:dist", 23 | "test": "mocha tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" --reporter progress", 24 | "test:cover": "babel-node node_modules/isparta/bin/isparta cover --root src --report html node_modules/mocha/bin/_mocha -- --require ./tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" --reporter progress", 25 | "test:cover:travis": "babel-node node_modules/isparta/bin/isparta cover --root src --report lcovonly _mocha -- --require ./tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" && cat ./coverage/lcov.info | node_modules/coveralls/bin/coveralls.js", 26 | "test:watch": "npm run test -- --watch", 27 | "open:cover": "npm run test:cover && open coverage/index.html" 28 | }, 29 | "author": "ajayns", 30 | "license": "MIT", 31 | "dependencies": { 32 | "bootstrap": "3.3.7", 33 | "object-assign": "4.1.0", 34 | "react": "15.3.2", 35 | "react-dom": "15.3.2", 36 | "react-redux": "4.4.5", 37 | "react-router": "2.8.1", 38 | "react-router-redux": "4.0.6", 39 | "redux": "3.6.0", 40 | "redux-thunk": "2.1.0" 41 | }, 42 | "devDependencies": { 43 | "autoprefixer": "6.5.1", 44 | "babel-cli": "6.16.0", 45 | "babel-core": "6.17.0", 46 | "babel-eslint": "7.0.0", 47 | "babel-loader": "6.2.5", 48 | "babel-plugin-react-display-name": "2.0.0", 49 | "babel-plugin-transform-react-constant-elements": "6.9.1", 50 | "babel-plugin-transform-react-remove-prop-types": "0.2.10", 51 | "babel-preset-latest": "6.16.0", 52 | "babel-preset-react": "6.16.0", 53 | "babel-preset-react-hmre": "1.1.1", 54 | "babel-preset-stage-1": "6.16.0", 55 | "babel-register": "6.16.3", 56 | "browser-sync": "2.17.5", 57 | "chai": "3.5.0", 58 | "chalk": "1.1.3", 59 | "connect-history-api-fallback": "1.3.0", 60 | "coveralls": "2.11.14", 61 | "cross-env": "3.1.3", 62 | "css-loader": "0.25.0", 63 | "enzyme": "2.5.1", 64 | "eslint": "3.8.1", 65 | "eslint-plugin-import": "2.0.1", 66 | "eslint-plugin-jsx-a11y": "2.2.3", 67 | "eslint-plugin-react": "6.4.1", 68 | "eslint-watch": "2.1.14", 69 | "extract-text-webpack-plugin": "1.0.1", 70 | "file-loader": "0.9.0", 71 | "html-webpack-plugin": "2.24.0", 72 | "isparta": "4.0.0", 73 | "istanbul": "0.4.4", 74 | "json-loader": "0.5.4", 75 | "mocha": "3.1.2", 76 | "mockdate": "1.0.4", 77 | "node-sass": "3.10.1", 78 | "npm-run-all": "3.1.1", 79 | "open": "0.0.5", 80 | "postcss-loader": "1.0.0", 81 | "prompt": "1.0.0", 82 | "react-addons-test-utils": "15.3.2", 83 | "redux-immutable-state-invariant": "1.2.4", 84 | "replace": "0.3.0", 85 | "rimraf": "2.5.4", 86 | "sass-loader": "4.0.2", 87 | "sinon": "1.17.6", 88 | "sinon-chai": "2.8.0", 89 | "style-loader": "0.13.1", 90 | "url-loader": "0.5.7", 91 | "webpack": "1.13.2", 92 | "webpack-bundle-analyzer": "1.4.1", 93 | "webpack-dev-middleware": "1.8.4", 94 | "webpack-hot-middleware": "2.13.0", 95 | "webpack-md5-hash": "0.0.5" 96 | }, 97 | "keywords": [], 98 | "repository": { 99 | "type": "git", 100 | "url": "" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /book-store/src/actions/bookActions.js: -------------------------------------------------------------------------------- 1 | export const createBook = (book) => { 2 | // Return action 3 | return { 4 | // Type of action 5 | type: 'CREATE_BOOK', 6 | // Payload 7 | book 8 | }; 9 | }; -------------------------------------------------------------------------------- /book-store/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | const App = (props) => { 5 | return ( 6 |
7 | 22 | {props.children} 23 |
24 | ); 25 | }; 26 | 27 | export default App; -------------------------------------------------------------------------------- /book-store/src/components/book/BookPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import * as bookActions from '../../actions/bookActions'; 4 | 5 | class Book extends React.Component { 6 | constructor(props) { 7 | // Pass props to parent 8 | super(props); 9 | } 10 | 11 | // Submit book handler 12 | submitBook(input) { 13 | this.props.createBook(input); 14 | } 15 | 16 | render() { 17 | let titleInput; 18 | return ( 19 |
20 |

Books

21 | 28 |
{ 29 | // Prevent form from redirect 30 | e.preventDefault(); 31 | // Call handler 32 | this.submitBook({title: titleInput.value}); 33 | // Reset form after submit 34 | e.target.reset(); 35 | } 36 | }> 37 | titleInput = node}/> 38 | 39 |
40 |
41 | ); 42 | } 43 | } 44 | 45 | // Maps state from store to props 46 | const mapStateToProps = (state) => { 47 | return { 48 | books: state.books 49 | }; 50 | }; 51 | 52 | // Maps actions to props 53 | const mapDispatchToProps = (dispatch) => { 54 | return { 55 | createBook: book => dispatch(bookActions.createBook(book)) 56 | } 57 | } 58 | 59 | export default connect(mapStateToProps, mapDispatchToProps)(Book); -------------------------------------------------------------------------------- /book-store/src/components/common/AboutPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
6 |

About Page

7 |

Random text bro

8 |
9 | ); 10 | }; 11 | 12 | export default About; -------------------------------------------------------------------------------- /book-store/src/components/common/HomePage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

Home Page

7 |

Random text here

8 |
9 | ); 10 | }; 11 | 12 | export default Home; -------------------------------------------------------------------------------- /book-store/src/components/emptyTest.spec.js: -------------------------------------------------------------------------------- 1 | // Must have at least one test file in this directory or Mocha will throw an error. -------------------------------------------------------------------------------- /book-store/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajayns/react-projects/f2529c7d6cbb78aaeba90fdaa48f663b48cdb8f8/book-store/src/favicon.ico -------------------------------------------------------------------------------- /book-store/src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | <% if (htmlWebpackPlugin.options.trackJSToken) { %> 14 | 15 | 16 | <% } %> 17 | 18 | 19 | 20 | Fusion Starter 21 | 22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /book-store/src/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | import React from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { render } from 'react-dom'; 5 | import { Router, browserHistory } from 'react-router'; 6 | import routes from './routes'; 7 | import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; 8 | 9 | import configureStore from './store/configureStore'; 10 | 11 | const store = configureStore(); 12 | 13 | render( 14 | 15 | 16 | , 17 | document.getElementById('app') 18 | ); -------------------------------------------------------------------------------- /book-store/src/reducers/bookReducers.js: -------------------------------------------------------------------------------- 1 | export default (state = [], action) => { 2 | switch(action.type) { 3 | // Check if action is create book 4 | case 'CREATE_BOOK': 5 | return [ 6 | ...state, 7 | Object.assign({}, action.book) 8 | ]; 9 | default: 10 | return state; 11 | } 12 | }; -------------------------------------------------------------------------------- /book-store/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | // Set up your root reducer here... 2 | import { combineReducers } from 'redux'; 3 | import books from "./bookReducers"; 4 | 5 | export default combineReducers({ 6 | books 7 | }); -------------------------------------------------------------------------------- /book-store/src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, IndexRoute } from 'react-router'; 3 | import Home from './components/common/HomePage'; 4 | import About from './components/common/AboutPage'; 5 | import Book from './components/book/BookPage'; 6 | import App from './components/App'; 7 | 8 | export default ( 9 | 10 | 11 | 12 | 13 | 14 | ); -------------------------------------------------------------------------------- /book-store/src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore } from 'redux'; 2 | import rootReducer from '../reducers'; 3 | 4 | export default function configureStore(initialState) { 5 | return createStore(rootReducer, initialState); 6 | } -------------------------------------------------------------------------------- /book-store/src/webpack-public-path.js: -------------------------------------------------------------------------------- 1 | // Dynamically set the webpack public path at runtime below 2 | // This magic global is used by webpack to set the public path at runtime. 3 | // The public path is set dynamically to avoid the following issues: 4 | // 1. https://github.com/coryhouse/react-slingshot/issues/205 5 | // 2. https://github.com/coryhouse/react-slingshot/issues/181 6 | // 3. https://github.com/coryhouse/react-slingshot/pull/125 7 | // Documentation: http://webpack.github.io/docs/configuration.html#output-publicpath 8 | // eslint-disable-next-line no-undef 9 | __webpack_public_path__ = window.location.protocol + "//" + window.location.host + "/"; 10 | -------------------------------------------------------------------------------- /book-store/tools/.yarnclean: -------------------------------------------------------------------------------- 1 | !browser-sync-ui/lib/plugins/history # need this for now because of https://github.com/yarnpkg/yarn/issues/1396#issuecomment-255965666 2 | -------------------------------------------------------------------------------- /book-store/tools/build.js: -------------------------------------------------------------------------------- 1 | // More info on Webpack's Node API here: https://webpack.github.io/docs/node.js-api.html 2 | // Allowing console calls below since this is a build file. 3 | /* eslint-disable no-console */ 4 | import webpack from 'webpack'; 5 | import config from '../webpack.config.prod'; 6 | import {chalkError, chalkSuccess, chalkWarning, chalkProcessing} from './chalkConfig'; 7 | 8 | process.env.NODE_ENV = 'production'; // this assures React is built in prod mode and that the Babel dev config doesn't apply. 9 | 10 | console.log(chalkProcessing('Generating minified bundle. This will take a moment...')); 11 | 12 | webpack(config).run((error, stats) => { 13 | if (error) { // so a fatal error occurred. Stop here. 14 | console.log(chalkError(error)); 15 | return 1; 16 | } 17 | 18 | const jsonStats = stats.toJson(); 19 | 20 | if (jsonStats.hasErrors) { 21 | return jsonStats.errors.map(error => console.log(chalkError(error))); 22 | } 23 | 24 | if (jsonStats.hasWarnings) { 25 | console.log(chalkWarning('Webpack generated the following warnings: ')); 26 | jsonStats.warnings.map(warning => console.log(chalkWarning(warning))); 27 | } 28 | 29 | console.log(`Webpack stats: ${stats}`); 30 | 31 | // if we got this far, the build succeeded. 32 | console.log(chalkSuccess('Your app is compiled in production mode in /dist. It\'s ready to roll!')); 33 | 34 | return 0; 35 | }); 36 | -------------------------------------------------------------------------------- /book-store/tools/chalkConfig.js: -------------------------------------------------------------------------------- 1 | // Centralized configuration for chalk, which is used to add color to console.log statements. 2 | import chalk from 'chalk'; 3 | export const chalkError = chalk.red; 4 | export const chalkSuccess = chalk.green; 5 | export const chalkWarning = chalk.yellow; 6 | export const chalkProcessing = chalk.blue; 7 | -------------------------------------------------------------------------------- /book-store/tools/distServer.js: -------------------------------------------------------------------------------- 1 | // This file configures a web server for testing the production build 2 | // on your local machine. 3 | 4 | import browserSync from 'browser-sync'; 5 | import historyApiFallback from 'connect-history-api-fallback'; 6 | import {chalkProcessing} from './chalkConfig'; 7 | 8 | /* eslint-disable no-console */ 9 | 10 | console.log(chalkProcessing('Opening production build...')); 11 | 12 | // Run Browsersync 13 | browserSync({ 14 | port: 3000, 15 | ui: { 16 | port: 3001 17 | }, 18 | server: { 19 | baseDir: 'dist' 20 | }, 21 | 22 | files: [ 23 | 'src/*.html' 24 | ], 25 | 26 | middleware: [historyApiFallback()] 27 | }); 28 | -------------------------------------------------------------------------------- /book-store/tools/nodeVersionCheck.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var exec = require('child_process').exec; 3 | 4 | exec('node -v', function (err, stdout) { 5 | if (err) throw err; 6 | 7 | if (parseFloat(stdout.slice(1)) < 4) { 8 | throw new Error('React Slingshot requires node 4.0 or greater.'); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /book-store/tools/removeDemo.js: -------------------------------------------------------------------------------- 1 | // This script removes demo app files 2 | import rimraf from 'rimraf'; 3 | import fs from 'fs'; 4 | import {chalkSuccess} from './chalkConfig'; 5 | 6 | /* eslint-disable no-console */ 7 | 8 | const pathsToRemove = [ 9 | './src/actions/*', 10 | './src/utils', 11 | './src/components/*', 12 | './src/constants/*', 13 | './src/containers/*', 14 | './src/images', 15 | './src/reducers/*', 16 | './src/store/store.spec.js', 17 | './src/styles', 18 | './src/routes.js', 19 | './src/index.js' 20 | ]; 21 | 22 | const filesToCreate = [ 23 | { 24 | path: './src/components/emptyTest.spec.js', 25 | content: '// Must have at least one test file in this directory or Mocha will throw an error.' 26 | }, 27 | { 28 | path: './src/index.js', 29 | content: '// Set up your application entry point here...' 30 | }, 31 | { 32 | path: './src/reducers/index.js', 33 | content: '// Set up your root reducer here...\n import { combineReducers } from \'redux\';\n export default combineReducers;' 34 | } 35 | ]; 36 | 37 | function removePath(path, callback) { 38 | rimraf(path, error => { 39 | if (error) throw new Error(error); 40 | callback(); 41 | }); 42 | } 43 | 44 | function createFile(file) { 45 | fs.writeFile(file.path, file.content, error => { 46 | if (error) throw new Error(error); 47 | }); 48 | } 49 | 50 | let numPathsRemoved = 0; 51 | pathsToRemove.map(path => { 52 | removePath(path, () => { 53 | numPathsRemoved++; 54 | if (numPathsRemoved === pathsToRemove.length) { // All paths have been processed 55 | // Now we can create files since we're done deleting. 56 | filesToCreate.map(file => createFile(file)); 57 | } 58 | }); 59 | }); 60 | 61 | console.log(chalkSuccess('Demo app removed.')); 62 | -------------------------------------------------------------------------------- /book-store/tools/srcServer.js: -------------------------------------------------------------------------------- 1 | // This file configures the development web server 2 | // which supports hot reloading and synchronized testing. 3 | 4 | // Require Browsersync along with webpack and middleware for it 5 | import browserSync from 'browser-sync'; 6 | // Required for react-router browserHistory 7 | // see https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643 8 | import historyApiFallback from 'connect-history-api-fallback'; 9 | import webpack from 'webpack'; 10 | import webpackDevMiddleware from 'webpack-dev-middleware'; 11 | import webpackHotMiddleware from 'webpack-hot-middleware'; 12 | import config from '../webpack.config.dev'; 13 | 14 | const bundler = webpack(config); 15 | 16 | // Run Browsersync and use middleware for Hot Module Replacement 17 | browserSync({ 18 | port: 3000, 19 | ui: { 20 | port: 3001 21 | }, 22 | server: { 23 | baseDir: 'src', 24 | 25 | middleware: [ 26 | historyApiFallback(), 27 | 28 | webpackDevMiddleware(bundler, { 29 | // Dev middleware can't access config, so we provide publicPath 30 | publicPath: config.output.publicPath, 31 | 32 | // These settings suppress noisy webpack output so only errors are displayed to the console. 33 | noInfo: false, 34 | quiet: false, 35 | stats: { 36 | assets: false, 37 | colors: true, 38 | version: false, 39 | hash: false, 40 | timings: false, 41 | chunks: false, 42 | chunkModules: false 43 | }, 44 | 45 | // for other settings see 46 | // http://webpack.github.io/docs/webpack-dev-middleware.html 47 | }), 48 | 49 | // bundler should be the same as above 50 | webpackHotMiddleware(bundler) 51 | ] 52 | }, 53 | 54 | // no need to watch '*.js' here, webpack will take care of it for us, 55 | // including full page reloads if HMR won't work 56 | files: [ 57 | 'src/*.html' 58 | ] 59 | }); 60 | -------------------------------------------------------------------------------- /book-store/tools/startMessage.js: -------------------------------------------------------------------------------- 1 | import {chalkSuccess} from './chalkConfig'; 2 | 3 | /* eslint-disable no-console */ 4 | 5 | console.log(chalkSuccess('Starting app in dev mode...')); 6 | -------------------------------------------------------------------------------- /book-store/tools/testSetup.js: -------------------------------------------------------------------------------- 1 | // Tests are placed alongside files under test. 2 | // This file does the following: 3 | // 1. Sets the environment to 'test' so that 4 | // dev-specific babel config in .babelrc doesn't run. 5 | // 2. Disables Webpack-specific features that Mocha doesn't understand. 6 | // 3. Registers babel for transpiling our code for testing. 7 | 8 | // This assures the .babelrc dev config (which includes 9 | // hot module reloading code) doesn't apply for tests. 10 | // Setting NODE_ENV to test instead of production because setting it to production will suppress error messaging 11 | // and propType validation warnings. 12 | process.env.NODE_ENV = 'test'; 13 | 14 | // Disable webpack-specific features for tests since 15 | // Mocha doesn't know what to do with them. 16 | ['.css', '.scss', '.png', '.jpg'].forEach(ext => { 17 | require.extensions[ext] = () => null; 18 | }); 19 | 20 | // Register babel so that it will transpile ES6 to ES5 21 | // before our tests run. 22 | require('babel-register')(); 23 | -------------------------------------------------------------------------------- /book-store/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 3 | import autoprefixer from 'autoprefixer'; 4 | import path from 'path'; 5 | import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; 6 | 7 | export default { 8 | resolve: { 9 | extensions: ['', '.js', '.jsx', '.json'] 10 | }, 11 | debug: true, 12 | devtool: 'eval-source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool 13 | noInfo: true, // set to false to see a list of every file being bundled. 14 | entry: [ 15 | // must be first entry to properly set public path 16 | './src/webpack-public-path', 17 | 'webpack-hot-middleware/client?reload=true', 18 | path.resolve(__dirname, 'src/index.js') // Defining path seems necessary for this to work consistently on Windows machines. 19 | ], 20 | target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test 21 | output: { 22 | path: path.resolve(__dirname, 'dist'), // Note: Physical files are only output by the production build task `npm run build`. 23 | publicPath: '/', 24 | filename: 'bundle.js' 25 | }, 26 | plugins: [ 27 | new webpack.DefinePlugin({ 28 | 'process.env.NODE_ENV': JSON.stringify('development'), // Tells React to build in either dev or prod modes. https://facebook.github.io/react/downloads.html (See bottom) 29 | __DEV__: true 30 | }), 31 | new webpack.HotModuleReplacementPlugin(), 32 | new webpack.NoErrorsPlugin(), 33 | new HtmlWebpackPlugin({ // Create HTML file that includes references to bundled CSS and JS. 34 | template: 'src/index.ejs', 35 | minify: { 36 | removeComments: true, 37 | collapseWhitespace: true 38 | }, 39 | inject: true 40 | }), 41 | new BundleAnalyzerPlugin() 42 | ], 43 | module: { 44 | loaders: [ 45 | {test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel']}, 46 | {test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'file'}, 47 | {test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url?limit=10000&mimetype=application/font-woff"}, 48 | {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'}, 49 | {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'}, 50 | {test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'}, 51 | {test: /\.ico$/, loader: 'file?name=[name].[ext]'}, 52 | {test: /(\.css|\.scss)$/, loaders: ['style', 'css?sourceMap', 'postcss', 'sass?sourceMap']}, 53 | {test: /\.json$/, loader: "json"} 54 | ] 55 | }, 56 | postcss: ()=> [autoprefixer] 57 | }; 58 | -------------------------------------------------------------------------------- /book-store/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | // For info about this file refer to webpack and webpack-hot-middleware documentation 2 | // For info on how we're generating bundles with hashed filenames for cache busting: https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.w99i89nsz 3 | import webpack from 'webpack'; 4 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 5 | import WebpackMd5Hash from 'webpack-md5-hash'; 6 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 7 | import autoprefixer from 'autoprefixer'; 8 | import path from 'path'; 9 | 10 | const GLOBALS = { 11 | 'process.env.NODE_ENV': JSON.stringify('production'), 12 | __DEV__: false 13 | }; 14 | 15 | export default { 16 | resolve: { 17 | extensions: ['', '.js', '.jsx', '.json'] 18 | }, 19 | debug: true, 20 | devtool: 'source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool 21 | noInfo: true, // set to false to see a list of every file being bundled. 22 | entry: path.resolve(__dirname, 'src/index'), 23 | target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test 24 | output: { 25 | path: path.resolve(__dirname, 'dist'), 26 | publicPath: '/', 27 | filename: '[name].[chunkhash].js' 28 | }, 29 | plugins: [ 30 | // Hash the files using MD5 so that their names change when the content changes. 31 | new WebpackMd5Hash(), 32 | 33 | // Optimize the order that items are bundled. This assures the hash is deterministic. 34 | new webpack.optimize.OccurenceOrderPlugin(), 35 | 36 | // Tells React to build in prod mode. https://facebook.github.io/react/downloads.html 37 | new webpack.DefinePlugin(GLOBALS), 38 | 39 | // Generate an external css file with a hash in the filename 40 | new ExtractTextPlugin('[name].[contenthash].css'), 41 | 42 | // Generate HTML file that contains references to generated bundles. See here for how this works: https://github.com/ampedandwired/html-webpack-plugin#basic-usage 43 | new HtmlWebpackPlugin({ 44 | template: 'src/index.ejs', 45 | minify: { 46 | removeComments: true, 47 | collapseWhitespace: true, 48 | removeRedundantAttributes: true, 49 | useShortDoctype: true, 50 | removeEmptyAttributes: true, 51 | removeStyleLinkTypeAttributes: true, 52 | keepClosingSlash: true, 53 | minifyJS: true, 54 | minifyCSS: true, 55 | minifyURLs: true 56 | }, 57 | inject: true, 58 | // Note that you can add custom options here if you need to handle other custom logic in index.html 59 | // To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below. 60 | trackJSToken: '' 61 | }), 62 | 63 | // Eliminate duplicate packages when generating bundle 64 | new webpack.optimize.DedupePlugin(), 65 | 66 | // Minify JS 67 | new webpack.optimize.UglifyJsPlugin() 68 | ], 69 | module: { 70 | loaders: [ 71 | {test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel'}, 72 | {test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'url?name=[name].[ext]'}, 73 | {test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url?limit=10000&mimetype=application/font-woff&name=[name].[ext]"}, 74 | {test: /\.ttf(\?v=\d+.\d+.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream&name=[name].[ext]'}, 75 | {test: /\.svg(\?v=\d+.\d+.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml&name=[name].[ext]'}, 76 | {test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'}, 77 | {test: /\.ico$/, loader: 'file?name=[name].[ext]'}, 78 | {test: /(\.css|\.scss)$/, loader: ExtractTextPlugin.extract('css?sourceMap!postcss!sass?sourceMap')}, 79 | {test: /\.json$/, loader: "json"} 80 | ] 81 | }, 82 | postcss: ()=> [autoprefixer] 83 | }; 84 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .cache/ 61 | public 62 | yarn-error.log 63 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 gatsbyjs 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 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/README.md: -------------------------------------------------------------------------------- 1 | # gatsby-starter-hello-world 2 | Starter with the bare essentials needed for a [Gatsby](https://www.gatsbyjs.org/) site 3 | 4 | Install this starter (assuming Gatsby is installed) by running from your CLI: 5 | ``` 6 | gatsby new gatsby-site https://github.com/gatsbyjs/gatsby-starter-hello-world 7 | ``` 8 | 9 | ## Running in development 10 | `gatsby develop` 11 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/gatsby-config.js: -------------------------------------------------------------------------------- 1 | // Configure plugins used 2 | // gatsby-source-filesystem is pointed to /src to look for files 3 | // typography.js is configured using /utils/typography 4 | 5 | module.exports = { 6 | siteMetadata: { 7 | title: `Pandas Eating Lots`, 8 | }, 9 | plugins: [ 10 | { 11 | resolve: `gatsby-source-filesystem`, 12 | options: { 13 | name: `src`, 14 | path: `${__dirname}/src/`, 15 | }, 16 | }, 17 | `gatsby-transformer-remark`, 18 | `gatsby-plugin-glamor`, 19 | { 20 | resolve: `gatsby-plugin-typography`, 21 | options: { 22 | pathToConfigModule: `src/utils/typography`, 23 | }, 24 | }, 25 | ], 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require(`path`); 2 | const { createFilePath } = require(`gatsby-source-filesystem`); 3 | 4 | // onCreateNode is called for every node created 5 | // markdown nodes are filtered and createNodeField adds a field slug which holds value of the filePath 6 | 7 | exports.onCreateNode = ({ node, getNode, boundActionCreators }) => { 8 | const { createNodeField } = boundActionCreators 9 | if (node.internal.type === `MarkdownRemark`) { 10 | const slug = createFilePath({ node, getNode, basePath: `pages` }) 11 | createNodeField({ 12 | node, 13 | name: `slug`, 14 | value: slug, 15 | }) 16 | } 17 | }; 18 | 19 | 20 | // createPages creates a page for each markdown node 21 | // createPage uses path from slug queried, blog-post.js as template to create page 22 | // Everything in context is passed into individual pages 23 | exports.createPages = ({ graphql, boundActionCreators }) => { 24 | const { createPage } = boundActionCreators 25 | return new Promise((resolve, reject) => { 26 | graphql(` 27 | { 28 | allMarkdownRemark { 29 | edges { 30 | node { 31 | fields { 32 | slug 33 | } 34 | } 35 | } 36 | } 37 | } 38 | `).then(result => { 39 | result.data.allMarkdownRemark.edges.forEach(({node}) => { 40 | createPage({ 41 | path: node.fields.slug, 42 | component: path.resolve(`./src/templates/blog-post.js`), 43 | context: { 44 | slug: node.fields.slug, 45 | }, 46 | }) 47 | }) 48 | resolve() 49 | }) 50 | }) 51 | }; -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsbyjs-markdown-blog", 3 | "description": "Gatsby blog made referring to tutorial 4 from the Official site.", 4 | "license": "MIT", 5 | "scripts": { 6 | "develop": "gatsby develop", 7 | "build": "gatsby build", 8 | "serve": "gatsby serve", 9 | "start": "gatsby develop" 10 | }, 11 | "dependencies": { 12 | "gatsby": "^1.9.158", 13 | "gatsby-link": "^1.6.34", 14 | "gatsby-plugin-glamor": "^1.6.12", 15 | "gatsby-plugin-typography": "^1.7.14", 16 | "gatsby-source-filesystem": "^1.5.19", 17 | "gatsby-transformer-remark": "^1.7.32", 18 | "glamorous": "^4.11.4", 19 | "typography-theme-kirkham": "^0.16.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Layout encloses everything else in itself for all pages 4 | // children is a function that contains all the pages 5 | // so all basically all pages follow this layout now 6 | export default ({children}) => ( 7 |
8 |

Panda Blog

9 |
10 | {children()} 11 |
12 |
13 | ) -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/pages/blog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import g from "glamorous"; 3 | import Link from "gatsby-link"; 4 | 5 | import { rhythm } from "../utils/typography"; 6 | 7 | // Displays list of blog posts (i.e markdown files) 8 | export default ({ data }) => { 9 | return ( 10 |
11 | 12 | Amazing Pandas Eating Things 13 | 14 |

{data.allMarkdownRemark.totalCount} Posts

15 | {data.allMarkdownRemark.edges.map(({ node }) => ( 16 |
17 | 21 | 22 | {node.frontmatter.title}{" "} 23 | — {node.frontmatter.date} 24 | 25 |

26 | {node.excerpt} 27 |

28 | 29 |
30 | ))} 31 |
32 | ); 33 | }; 34 | 35 | // gatsby-transformer-remark converts each markdown file into JSON data with content as HTML 36 | // GraphQL queries for the list of such files 37 | export const query = graphql` 38 | query IndexQuery { 39 | allMarkdownRemark { 40 | totalCount 41 | edges { 42 | node { 43 | id 44 | frontmatter { 45 | title 46 | date(formatString: "DD MMMM, YYYY") 47 | } 48 | fields { 49 | slug 50 | } 51 | excerpt 52 | } 53 | } 54 | } 55 | } 56 | `; -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "gatsby-link"; 3 | 4 | // Basic boilerplate code for content to display 5 | export default () => ( 6 |
7 |

Hi! I'm building a fake Gatsby site as part of a tutorial!

8 |

9 | What do I like to do? Lots of course but definitely enjoy building 10 | websites. 11 |

12 | Go to blog 13 |
14 | ); 15 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/pages/pandas-and-bananas.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pandas and Bananas 3 | date: "2017-08-21" 4 | --- 5 | 6 | Do Pandas eat bananas? Check out this short video that shows that yes! pandas do 7 | seem to really enjoy bananas! 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/pages/sweet-pandas-eating-sweets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sweet Pandas Eating Sweets" 3 | date: "2017-08-10" 4 | --- 5 | 6 | Pandas are really sweet. 7 | 8 | Here's a video of a panda eating sweets. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/templates/blog-post.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // data is returned from GraphQL query 4 | export default ({ data }) => { 5 | const post = data.markdownRemark; 6 | return ( 7 |
8 |

{post.frontmatter.title}

9 |
10 |
11 | ); 12 | }; 13 | 14 | // slug passed in from context, used to find current post 15 | export const query = graphql` 16 | query BlogPostQuery($slug: String!) { 17 | markdownRemark(fields: { slug: { eq: $slug } }) { 18 | html 19 | frontmatter { 20 | title 21 | } 22 | } 23 | } 24 | `; 25 | 26 | -------------------------------------------------------------------------------- /gatsbyjs-markdown-blog/src/utils/typography.js: -------------------------------------------------------------------------------- 1 | import Typography from 'typography'; 2 | import kirkhamTheme from 'typography-theme-kirkham' 3 | 4 | const typography = new Typography(kirkhamTheme); 5 | 6 | export default typography; -------------------------------------------------------------------------------- /markdown-editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-markdown-editor", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.2.0", 7 | "react-dom": "^16.2.0", 8 | "react-scripts": "1.0.17", 9 | "remarkable": "^1.7.1" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test --env=jsdom", 15 | "eject": "react-scripts eject" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /markdown-editor/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajayns/react-projects/f2529c7d6cbb78aaeba90fdaa48f663b48cdb8f8/markdown-editor/public/favicon.ico -------------------------------------------------------------------------------- /markdown-editor/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | Markdown Editor 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /markdown-editor/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /markdown-editor/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 14px Segoe, sans-serif; 3 | margin: 0 auto; 4 | background: #212121; 5 | color: white; 6 | } 7 | 8 | .container { 9 | display: flex; 10 | justify-content: center; 11 | height: 100vh; 12 | } 13 | 14 | .input, .output { 15 | width: 50%; 16 | padding: 2em; 17 | box-sizing: border-box; 18 | } 19 | 20 | .input-text { 21 | min-height: 400px; 22 | border: 2px solid #464646; 23 | background: transparent; 24 | color: white; 25 | transition: border 0.3s; 26 | } 27 | 28 | .input-text, .output-text { 29 | width: 100%; 30 | box-sizing: border-box; 31 | min-height: 400px; 32 | border-radius: 4px; 33 | padding: 1em; 34 | } 35 | 36 | .output-text { 37 | background: #464646; 38 | overflow-wrap: break-word; 39 | word-wrap: break-word; 40 | hyphens: auto; 41 | } 42 | 43 | .input-text:focus { 44 | border: 2px solid grey; 45 | } 46 | 47 | h3 { 48 | font-weight: normal; 49 | } 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /markdown-editor/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import Remarkable from 'remarkable'; 5 | 6 | class MarkdownEditor extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.handleChange = this.handleChange.bind(this); 10 | this.state = { 11 | value: 'Type some *markdown* here!' 12 | }; 13 | } 14 | 15 | 16 | handleChange(e) { 17 | this.setState({ 18 | value: e.target.value 19 | }); 20 | } 21 | 22 | getRawMarkup() { 23 | const md = new Remarkable(); 24 | return {__html: md.render(this.state.value)}; 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 |
31 |

Input

32 |