├── .browserslistrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .prettierrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── client ├── .babelrc ├── .eslintrc ├── README.md ├── __setup__ │ ├── enzyme.js │ └── shim.js ├── components │ ├── __mocks__ │ │ ├── context-hoc.js │ │ └── styles.js │ ├── about │ │ ├── About.jsx │ │ ├── About.spec.js │ │ ├── __snapshots__ │ │ │ └── About.spec.js.snap │ │ ├── about.scss │ │ └── index.js │ ├── app │ │ ├── App.jsx │ │ ├── App.spec.js │ │ ├── __snapshots__ │ │ │ └── App.spec.js.snap │ │ ├── app.scss │ │ └── index.js │ ├── clickable │ │ ├── Clickable.jsx │ │ ├── Clickable.spec.js │ │ ├── __snapshots__ │ │ │ └── Clickable.spec.js.snap │ │ ├── clickable.scss │ │ └── index.js │ ├── counter │ │ ├── Counter.jsx │ │ ├── Counter.spec.js │ │ ├── __snapshots__ │ │ │ └── Counter.spec.js.snap │ │ ├── counter.scss │ │ └── index.js │ ├── nav │ │ ├── Nav.jsx │ │ ├── Nav.spec.js │ │ ├── __snapshots__ │ │ │ └── Nav.spec.js.snap │ │ ├── index.js │ │ └── nav.scss │ └── not-found │ │ ├── NotFound.jsx │ │ ├── NotFound.spec.js │ │ ├── __snapshots__ │ │ └── NotFound.spec.js.snap │ │ ├── index.js │ │ └── not-found.scss ├── config │ ├── webpack.config.common.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpack.config.test.js ├── favicon.ico ├── index.jsx ├── index.scss ├── package.json ├── postcss.config.js ├── routes │ ├── Root.js │ └── links.js ├── styles │ ├── _mixins.scss │ └── _palette.scss ├── utils │ ├── __mocks__ │ │ └── fetch-response.mock.js │ ├── __tests__ │ │ ├── classes.spec.js │ │ ├── fetch.spec.js │ │ └── noop.spec.js │ ├── classes.js │ ├── fetch.js │ └── noop.js ├── webpack.config.js └── yarn.lock ├── docs ├── issue_template.md ├── pull_request_template.md ├── setup.md └── vscode.md ├── package.json ├── plop-templates └── component │ ├── {{ dashCase name }}.scss │ ├── {{ pascalCase name }}.jsx │ └── {{ pascalCase name }}.spec.js ├── plopfile.js ├── server ├── README.md ├── app.dev.js ├── app.prod.js ├── app.test.js ├── config │ └── index.js ├── datastore │ ├── connection │ │ └── knexfile.js │ ├── constants │ │ └── postgres-errors.js │ ├── create │ │ ├── knexfile.js │ │ └── seeds │ │ │ └── create.js │ ├── migrations │ │ ├── 20170518181737_create_sample_table.js │ │ └── 20170613154653_create_lookup_table.js │ ├── seeds │ │ └── populate_sample_table.js │ ├── server │ │ └── docker-compose.yml │ └── stores │ │ ├── base.store.js │ │ ├── lookup.store.js │ │ └── sample.store.js ├── exceptions │ ├── bad-request.exception.js │ ├── generic.exception.js │ ├── index.js │ ├── not-found.exception.js │ └── validation.exception.js ├── index.js ├── package.json ├── routes │ ├── index.js │ └── sample.route.js ├── services │ └── sample.service.js ├── tests │ ├── config.js │ └── sample.test.js ├── utils │ ├── app.configure.js │ └── logger.js └── yarn.lock └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | # Supported browsers 2 | last 1 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 4 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [yarn.lock] 18 | end_of_line = crlf 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | "parser": "babel-eslint", 6 | "parserOptions": { 7 | "ecmaVersion": 6, 8 | "sourceType": "module" 9 | }, 10 | "env": { 11 | "node": true, 12 | "es6": true 13 | }, 14 | "rules": { 15 | "no-console": [2, { 16 | "allow": ["error", "warn", "info"] 17 | }], 18 | 19 | // Possible Errors 20 | "comma-dangle": 1, // disallow trailing commas in object literals 21 | "no-cond-assign": 1, // disallow assignment in conditional expressions 22 | "no-constant-condition": 0, // disallow use of constant expressions in conditions 23 | "no-control-regex": 0, // disallow control characters in regular expressions 24 | "no-debugger": 1, // disallow use of debugger 25 | "no-dupe-keys": 1, // disallow duplicate keys when creating object literals 26 | "no-empty": 1, // disallow empty statements 27 | "no-empty-character-class": 1, // disallow the use of empty character classes in regular expressions 28 | "no-ex-assign": 0, // disallow assigning to the exception in a catch block 29 | "no-extra-boolean-cast": 0, // disallow double-negation boolean casts in a boolean context 30 | "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) 31 | "no-extra-semi": 1, // disallow unnecessary semicolons 32 | "no-func-assign": 0, // disallow overwriting functions written as function declarations 33 | "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks 34 | "no-invalid-regexp": 0, // disallow invalid regular expression strings in the RegExp constructor 35 | "no-irregular-whitespace": 1, // disallow irregular whitespace outside of strings and comments 36 | "no-negated-in-lhs": 0, // disallow negation of the left operand of an in expression 37 | "no-obj-calls": 0, // disallow the use of object properties of the global object (Math and JSON) as functions 38 | "no-regex-spaces": 0, // disallow multiple spaces in a regular expression literal 39 | "quote-props": 0, // disallow reserved words being used as object literal keys (off by default) 40 | "no-sparse-arrays": 0, // disallow sparse arrays 41 | "no-unreachable": 0, // disallow unreachable statements after a return, throw, continue, or break statement 42 | "use-isnan": 0, // disallow comparisons with the value NaN 43 | "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default) 44 | "valid-typeof": 0, // Ensure that the results of typeof are compared against a valid string 45 | // Best Practices 46 | "block-scoped-var": 1, // treat var statements as if they were block scoped (off by default) 47 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) 48 | "consistent-return": 0, // require return statements to either always or never specify values 49 | "curly": 1, // specify curly brace conventions for all control statements 50 | "default-case": 1, // require default case in switch statements (off by default) 51 | "dot-notation": 0, // encourages use of dot notation whenever possible 52 | "eqeqeq": 1, // require the use of === and !== 53 | "guard-for-in": 0, // make sure for-in loops have an if statement (off by default) 54 | "no-alert": 0, // disallow the use of alert, confirm, and prompt 55 | "no-caller": 1, // disallow use of arguments.caller or arguments.callee 56 | "no-div-regex": 0, // disallow division operators explicitly at beginning of regular expression (off by default) 57 | "no-else-return": 0, // disallow else after a return in an if (off by default) 58 | "no-labels": 1, // disallow use of labels for anything other then loops and switches 59 | "no-eq-null": 1, // disallow comparisons to null without a type-checking operator (off by default) 60 | "no-eval": 1, // disallow use of eval() 61 | "no-extend-native": 0, // disallow adding to native types 62 | "no-extra-bind": 0, // disallow unnecessary function binding 63 | "no-fallthrough": 0, // disallow fallthrough of case statements 64 | "no-floating-decimal": 0, // disallow the use of leading or trailing decimal points in numeric literals (off by default) 65 | "no-implied-eval": 0, // disallow use of eval()-like methods 66 | "no-iterator": 0, // disallow usage of __iterator__ property 67 | "no-lone-blocks": 0, // disallow unnecessary nested blocks 68 | "no-loop-func": 1, // disallow creation of functions within loops 69 | "no-multi-spaces": 1, // disallow use of multiple spaces 70 | "no-multi-str": 1, // disallow use of multiline strings 71 | "no-native-reassign": 0, // disallow reassignments of native objects 72 | "no-new": 0, // disallow use of new operator when not part of the assignment or comparison 73 | "no-new-func": 0, // disallow use of new operator for Function object 74 | "no-new-wrappers": 0, // disallows creating new instances of String, Number, and Boolean 75 | "no-octal": 0, // disallow use of octal literals 76 | "no-octal-escape": 0, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 77 | "no-process-env": 0, // disallow use of process.env (off by default) 78 | "no-proto": 0, // disallow usage of __proto__ property 79 | "no-redeclare": 1, // disallow declaring the same variable more then once 80 | "no-return-assign": 1, // disallow use of assignment in return statement 81 | "no-script-url": 0, // disallow use of javascript: urls. 82 | "no-self-compare": 1, // disallow comparisons where both sides are exactly the same (off by default) 83 | "no-sequences": 0, // disallow use of comma operator 84 | "no-unused-expressions": 0, // disallow usage of expressions in statement position 85 | "no-void": 0, // disallow use of void operator (off by default) 86 | "no-warning-comments": 0, // disallow usage of configurable warning terms in comments, e.g. TODO or FIXME (off by default) 87 | "no-with": 1, // disallow use of the with statement 88 | "radix": 0, // require use of the second argument for parseInt() (off by default) 89 | "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default) 90 | "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default) 91 | "yoda": 1, // require or disallow Yoda conditions 92 | // Strict Mode 93 | "global-strict": 0, // (deprecated) require or disallow the "use strict" pragma in the global scope (off by default in the node environment) 94 | "no-extra-strict": 0, // (deprecated) disallow unnecessary use of "use strict"; when already in strict mode 95 | "strict": 0, // controls location of Use Strict Directives 96 | // Variables 97 | "no-catch-shadow": 0, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 98 | "no-delete-var": 0, // disallow deletion of variables 99 | "no-label-var": 0, // disallow labels that share a name with a variable 100 | "no-shadow": 0, // disallow declaration of variables already declared in the outer scope 101 | "no-shadow-restricted-names": 0, // disallow shadowing of names such as arguments 102 | "no-undef": 0, // disallow use of undeclared variables unless mentioned in a /*global */ block 103 | "no-undef-init": 0, // disallow use of undefined when initializing variables 104 | "no-undefined": 0, // disallow use of undefined variable (off by default) 105 | "no-unused-vars": 2, // disallow declaration of variables that are not used in the code 106 | "no-use-before-define": 0, // disallow use of variables before they are defined 107 | // Stylistic Issues 108 | "brace-style": 1, // enforce one true brace style (off by default) 109 | "camelcase": 0, // require camel case names 110 | "comma-spacing": 0, // enforce spacing before and after comma 111 | "comma-style": 1, // enforce one true comma style (off by default) 112 | "consistent-this": 0, // enforces consistent naming when capturing the current execution context (off by default) 113 | "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines 114 | "func-names": 0, // require function expressions to have a name (off by default) 115 | "func-style": 0, // enforces use of function declarations or expressions (off by default) 116 | "key-spacing": 0, // enforces spacing between keys and values in object literal properties 117 | "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default) 118 | "new-cap": 0, // require a capital letter for constructors 119 | "new-parens": 0, // disallow the omission of parentheses when invoking a constructor with no arguments 120 | "no-array-constructor": 0, // disallow use of the Array constructor 121 | "no-inline-comments": 0, // disallow comments inline after code (off by default) 122 | "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default) 123 | "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation 124 | "no-multiple-empty-lines": 1, // disallow multiple empty lines (off by default) 125 | "no-nested-ternary": 1, // disallow nested ternary expressions (off by default) 126 | "no-new-object": 0, // disallow use of the Object constructor 127 | "semi-spacing": 1, // disallow space before semicolon 128 | "no-spaced-func": 1, // disallow space between function identifier and application 129 | "no-ternary": 0, // disallow the use of ternary operators (off by default) 130 | "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines 131 | "no-underscore-dangle": 0, // disallow dangling underscores in identifiers 132 | "no-wrap-func": 0, // disallow wrapping of non-IIFE statements in parens 133 | "one-var": 0, // allow just one var statement per function (off by default) 134 | "operator-assignment": 0, // require assignment operator shorthand where possible or prohibit it entirely (off by default) 135 | "padded-blocks": 0, // enforce padding within blocks (off by default) 136 | "quotes": [2, "single", { 137 | "avoidEscape": true 138 | }], // specify whether double or single quotes should be used 139 | "semi": 0, // require or disallow use of semicolons instead of ASI 140 | "sort-vars": 0, // sort variables within the same declaration block (off by default) 141 | "space-after-function-name": 0, // require a space after function names (off by default) 142 | "space-after-keywords": 0, // require a space after certain keywords (off by default) 143 | "space-before-blocks": 2, // require or disallow space before blocks (off by default) 144 | "object-curly-spacing": 0, // require or disallow spaces inside brackets (off by default) 145 | "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default) 146 | "space-infix-ops": 1, // require spaces around operators 147 | "space-return-throw-case": 0, // require a space after return, throw, and case 148 | "space-unary-ops": 0, // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 149 | "spaced-comment": 1, // require or disallow a space immediately following the // in a line comment (off by default) 150 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) 151 | // ECMAScript 6 152 | "no-var": 0, // require let or const instead of var (off by default) 153 | "generator-star": 0, // enforce the position of the * in generator functions (off by default) 154 | // Legacy 155 | "max-depth": 0, // specify the maximum depth that blocks can be nested (off by default) 156 | "max-len": [1, 120, 4, { 157 | "ignoreUrls": true 158 | }], // specify the maximum length of a line in your program (off by default) 159 | "max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default) 160 | "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) 161 | "no-bitwise": 0, // disallow use of bitwise operators (off by default) 162 | "no-plusplus": 0 // disallow use of unary operators, ++ and -- (off by default) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /build 6 | /tmp 7 | 8 | # dependencies 9 | /node_modules 10 | /client/node_modules 11 | /server/node_modules 12 | /bower_components 13 | 14 | # IDEs and editors 15 | /.idea 16 | /.vscode 17 | .project 18 | .classpath 19 | *.launch 20 | .settings/ 21 | 22 | # misc 23 | /.sass-cache 24 | /connect.lock 25 | /coverage/* 26 | client/coverage/* 27 | .coveralls.yml 28 | /libpeerconnection.log 29 | npm-debug.log 30 | yarn-error.log 31 | testem.log 32 | /typings 33 | stats.json 34 | 35 | # e2e 36 | /e2e/*.js 37 | /e2e/*.map 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | 43 | # cache 44 | .eslintcache 45 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "printWidth": 120, 5 | "jsxBracketSameLine": false, 6 | "semi": true, 7 | "singleQuote": true 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - 8 5 | install: yarn 6 | script: yarn coveralls || yarn test:client 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing · `react-seed` 2 | 3 | First off, thank you for considering contributing to this *React Seed*. 4 | It’s people like you that helps to spread the knowledge and make this repo such a great tool... 5 | 6 | We are building it for both, people who want to learn more about these technologies and teams starting new projects. 7 | 8 | In order to achieve this goal, we are keeping the main branch (`master`) as basic as possible: 9 | * Adding only what we consider essential for starting projects and newcomers to the *React* ecosystem. 10 | * Leaving other features (more advanced ones) to the `seed/*` branches. (read README's [Seed Branches](./README.md#seed-branches)) 11 | 12 | What we try to do is to have a simple seed, to fit most of the starting projects. 13 | In our experience a lot of existing seeds have features we don’t need, and therefore we end up removing. 14 | 15 | All the contributors will be listed in the `README` to make public their help and express our gratitude. 16 | 17 | ## 1. Set up your environment 18 | 19 | Read our documentation on [docs/setup](docs/setup.md). 20 | Once you have the environment up and running, start playing around and build something new in top of it. 21 | 22 | ## 2. Create a Pull Request 23 | 24 | If you like to contribute, submit a new *PR*. We will get back to you as soon as possible. 25 | 26 | See README's [Coming up next](./README.md#coming-up-next) section to get some ideas/features we want to incorporate. 27 | 28 | You can also see our pull request template [here](docs/pull_request_template.md). 29 | 30 | ## 3. Report a Bug 31 | 32 | Let us know if you found a bug, have some new ideas to include, etc. 33 | 34 | See our issue template [here](docs/issue_template.md). 35 | 36 | ## Team 37 | 38 | We maintain this project in our free time. 39 | We enjoy to contribute to the community. 40 | 41 | If you liked the project, don't forget to star it! 42 | Any help from community is appreciated. 43 | 44 | Thanks! 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 UruIT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-seed 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/UruIT/react-seed/develop/LICENSE) 4 | [![Build Status](https://travis-ci.org/UruIT/react-seed.svg)](https://travis-ci.org/UruIT/react-seed?branch=master) 5 | [![Coverage Status](https://coveralls.io/repos/github/UruIT/react-seed/badge.svg)](https://coveralls.io/github/UruIT/react-seed) 6 | [![GitHub release](https://img.shields.io/github/release/uruit/react-seed.svg)](https://github.com/UruIT/react-seed/releases) 7 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests) 8 | [![DevDependencies](https://img.shields.io/david/dev/uruit/react-seed.svg)](https://david-dm.org/uruit/react-seed?type=dev) 9 | 10 | [![Twitter Follow](https://img.shields.io/twitter/follow/uruit.svg?style=social&label=Follow)](https://twitter.com/UruIT/followers) 11 | [![Twitter URL](https://img.shields.io/twitter/url/http/uruit.react-seed.svg?style=social)](https://twitter.com/intent/tweet?text=react-seed%20by%20%40UruIT%20on&url=https%3A%2F%2Fgithub.com%2Furuit%2Freact-seed&hashtags=react,uruit,dev,seed) 12 | 13 | 14 | UruIT seed project for ReactJS applications 15 | 16 | 17 | ## Stack 18 | 19 | * Webpack 20 | * ES6/7 21 | * ESLint 22 | * React 16 23 | * Fetch 24 | * React-Router 25 | * Jest 26 | * Sass 27 | * Express 28 | * Git hooks 29 | * Plop 30 | 31 | 32 | ## Secondary features 33 | 34 | * Redux 35 | * Redux-Segment 36 | * MongoDB 37 | * MSSQL 38 | * i18n 39 | * PDF 40 | 41 | 42 | ## Seed Branches 43 | 44 | ``` 45 | master * 46 | seed/ 47 | ./redux 48 | ./mongo 49 | ./mssql 50 | ./redux-i18n 51 | ./i18n 52 | ./offline 53 | ``` 54 | 55 | ### Coming up next 56 | 57 | * server side rendering 58 | * graphQL 59 | * storybook 60 | * bootstrap 61 | 62 | ## Structure 63 | 64 | ``` 65 | app 66 | ├── client 67 | │ ├── components 68 | │ │ └── home 69 | │ ├── config 70 | │ │ └── webpack 71 | │ ├── routes 72 | │ ├── styles 73 | │ └── utils 74 | ├── docs 75 | └── server 76 | ├── config 77 | ├── datastore 78 | ├── exceptions 79 | ├── routes 80 | ├── services 81 | ├── tests 82 | └── utils 83 | ``` 84 | 85 | ## Development 86 | 87 | Restore all packages and start development server: 88 | 89 | ```bash 90 | yarn 91 | yarn dev 92 | ``` 93 | 94 | Open browser on [localhost:4000](http://localhost:4000/) 95 | 96 | 97 | ## Docs 98 | 99 | * [Project setup info here](docs/setup.md) 100 | * [Client Readme](client/README.md) 101 | * [Server Readme](server/README.md) 102 | * [VS Code](docs/vscode.md) 103 | 104 | ## Contributors 105 | 106 | * [Arnold Gandarillas Castillo](https://github.com/arkgast) 107 | 108 | ## Authors 109 | 110 | [carloluis](https://github.com/carloluis) |[matiasdelgado](https://github.com/matiasdelgado) |[rrivem](https://github.com/rrivem) |[marina-acosta](https://github.com/marina-acosta) | 111 | :---: |:---: |:---: |:---: | 112 | [carloluis](https://github.com/carloluis) |[matiasdelgado](https://github.com/matiasdelgado) |[rrivem](https://github.com/rrivem) |[marina-acosta](https://github.com/marina-acosta) 113 | 114 | ## License 115 | 116 | Licensed under the MIT License, Copyright © 2017 [UruIT](https://twitter.com/UruIT). 117 | 118 | See [LICENSE](./LICENSE) for more information. 119 | -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "survivejs-kanban" 6 | ], 7 | "env": { 8 | "start": { 9 | "presets": [ 10 | "react-hmre" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", "plugin:react/recommended" 4 | ], 5 | "parser": "babel-eslint", 6 | "parserOptions": { 7 | "ecmaFeatures": { 8 | "jsx": true 9 | } 10 | }, 11 | "env": { 12 | "browser": true, 13 | "jest": true 14 | }, 15 | "plugins": [ 16 | "react" 17 | ], 18 | "rules": { 19 | "jsx-quotes": [1, "prefer-double"], 20 | 21 | "react/jsx-no-undef": 1, 22 | "react/jsx-uses-react": 1, 23 | "react/jsx-uses-vars": 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React Seed Client 2 | 3 | 4 | ## Dependencies 5 | 6 | Add new packages to the `client` project: 7 | 8 | ```bash 9 | yarn add 10 | ``` 11 | -------------------------------------------------------------------------------- /client/__setup__/enzyme.js: -------------------------------------------------------------------------------- 1 | import './shim'; 2 | import Enzyme from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-16'; 4 | 5 | Enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /client/__setup__/shim.js: -------------------------------------------------------------------------------- 1 | const raf = (global.requestAnimationFrame = cb => setTimeout(cb, 0)); 2 | 3 | export default raf; 4 | -------------------------------------------------------------------------------- /client/components/__mocks__/context-hoc.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const withContext = (context = {}) => Wrapped => { 5 | class Wrapper extends Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | getChildContext() { 10 | return context; 11 | } 12 | render() { 13 | return ; 14 | } 15 | } 16 | 17 | let contextProps = Object.keys(context); 18 | Wrapper.childContextTypes = Object.assign( 19 | ...contextProps.map(prop => ({ [prop]: PropTypes.any })) 20 | ); 21 | 22 | return Wrapper; 23 | }; 24 | 25 | export default withContext; 26 | -------------------------------------------------------------------------------- /client/components/__mocks__/styles.js: -------------------------------------------------------------------------------- 1 | const identityObject = new Proxy({}, { 2 | get: (target, prop) => prop 3 | }); 4 | 5 | export default identityObject; 6 | -------------------------------------------------------------------------------- /client/components/about/About.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Counter from '../counter'; 3 | import styles from './about.scss'; 4 | 5 | const About = () => ( 6 |
7 |

React Seed

8 |

Simple project to start with React!

9 | 10 |
11 | ); 12 | 13 | export default About; 14 | -------------------------------------------------------------------------------- /client/components/about/About.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import About from './About'; 4 | 5 | describe('', () => { 6 | it('snapshot', () => { 7 | const renderer = new ShallowRenderer(); 8 | const tree = renderer.render(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /client/components/about/__snapshots__/About.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 |
7 |

8 | React Seed 9 |

10 |

11 | Simple project to start with React! 12 |

13 | 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /client/components/about/about.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | 3 | .about { 4 | text-align: center; 5 | 6 | h1, h2 { 7 | color: $black-2; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/components/about/index.js: -------------------------------------------------------------------------------- 1 | import About from './About'; 2 | 3 | export default About; 4 | -------------------------------------------------------------------------------- /client/components/app/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getJson } from 'utils/fetch'; 3 | import links from '../../routes/links'; 4 | import Clickable from '../clickable'; 5 | import styles from './app.scss'; 6 | import classes from 'utils/classes'; 7 | 8 | class App extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | this.handleClick = this.handleClick.bind(this); 13 | this.state = { 14 | jokes: [], 15 | sample: [], 16 | error: '' 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | getJson(links.api.sample) 22 | .then(sample => this.setState({ sample })) 23 | .catch(error => this.setState({ error })); 24 | } 25 | 26 | handleClick() { 27 | return getJson(links.chucknorris).then(response => 28 | this.setState({ 29 | jokes: [response.value, ...this.state.jokes] 30 | }) 31 | ); 32 | } 33 | 34 | render() { 35 | const { jokes, sample, error } = this.state; 36 | const jsxJokes = jokes.map((joke, idx) => ); 37 | 38 | return ( 39 |
40 | 43 |
{jsxJokes}
44 | {this.renderApiTest(sample, error)} 45 |
46 | ); 47 | } 48 | 49 | renderApiTest(sample, error) { 50 | const response = JSON.stringify(sample.length ? sample : error); 51 | 52 | return ( 53 | 56 | ~{links.api.sample} response: 57 |
{response}
58 | 59 | } 60 | /> 61 | ); 62 | } 63 | } 64 | 65 | export default App; 66 | -------------------------------------------------------------------------------- /client/components/app/App.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import { shallow, mount } from 'enzyme'; 4 | 5 | import { getJson } from 'utils/fetch'; 6 | import Clickable from '../clickable'; 7 | import links from '../../routes/links'; 8 | import App from './App'; 9 | 10 | jest.mock('utils/fetch', () => ({ 11 | getJson: jest.fn(url => new Promise((resolve, reject) => (url ? resolve({ value: 'another joke' }) : reject()))) 12 | })); 13 | 14 | describe('', () => { 15 | it('snapshot', () => { 16 | const renderer = new ShallowRenderer(); 17 | const tree = renderer.render(); 18 | expect(tree).toMatchSnapshot(); 19 | }); 20 | }); 21 | 22 | describe('App - button onClick', () => { 23 | const app = shallow(); 24 | 25 | beforeEach(() => { 26 | getJson.mockImplementation(() => Promise.resolve({ value: 'another joke' })); 27 | app.find('button').simulate('click'); 28 | }); 29 | 30 | it('should call ChuckNorris api', () => { 31 | expect(getJson).toBeCalledWith(links.chucknorris); 32 | }); 33 | 34 | it('should keep previous jokes', () => { 35 | expect(app.state().jokes.length).toBe(2); 36 | }); 37 | 38 | it('should update children', () => { 39 | return app 40 | .instance() 41 | .handleClick() 42 | .then(() => 43 | expect( 44 | app 45 | .find(Clickable) 46 | .first() 47 | .props().content 48 | ).toEqual('another joke') 49 | ); 50 | }); 51 | }); 52 | 53 | describe('App - mounting', () => { 54 | describe('server request succeeded', () => { 55 | const sample = [{ id: 1 }, { id: 2 }]; 56 | getJson.mockImplementation(() => Promise.resolve(sample)); 57 | const tree = mount(); 58 | 59 | it('should call getJson', () => { 60 | expect(getJson).toBeCalledWith(links.api.sample); 61 | }); 62 | 63 | it('should update state.sample', () => { 64 | expect(tree.instance().state).toMatchObject({ sample }); 65 | }); 66 | }); 67 | 68 | describe('server request error', () => { 69 | getJson.mockImplementation(() => Promise.reject({ code: 500 })); 70 | const app = mount(); 71 | 72 | it('should update state.error', () => { 73 | expect(app.instance().state).toMatchObject({ error: { code: 500 } }); 74 | }); 75 | 76 | it('should not have any joke', () => { 77 | expect(app.state().jokes).toEqual([]); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /client/components/app/__snapshots__/App.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 |
7 | 13 |
16 | 21 | 22 | ~ 23 | /api/sample 24 | response: 25 | 26 |
27 | "" 28 |
29 |
30 | } 31 | onClick={[Function]} 32 | /> 33 |
34 | `; 35 | -------------------------------------------------------------------------------- /client/components/app/app.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_mixins'; 2 | @import '~styles/_palette'; 3 | 4 | $narrow-screen: 420px; 5 | 6 | .shadow { 7 | @include box-shadow(1px, 1px, 4px, $slate); 8 | } 9 | 10 | .container { 11 | max-width: 400px; 12 | margin: 24px auto 8px; 13 | padding: 8px; 14 | text-align: center; 15 | 16 | .button { 17 | border: 2px solid $black; 18 | background-color: $gold; 19 | cursor: pointer; 20 | font-weight: 500; 21 | font-size: 2rem; 22 | width: 100%; 23 | 24 | &:focus { 25 | outline: 0; 26 | } 27 | } 28 | 29 | .jokes { 30 | margin: 16px 0; 31 | } 32 | } 33 | 34 | .api-response { 35 | padding: 4px; 36 | 37 | span { 38 | display: inline-block; 39 | text-decoration: underline; 40 | margin-bottom: 4px; 41 | } 42 | 43 | div { 44 | overflow: auto; 45 | } 46 | } 47 | 48 | @media screen and (min-width: $narrow-screen) { 49 | .container { 50 | padding: 4px; 51 | max-width: $narrow-screen; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /client/components/app/index.js: -------------------------------------------------------------------------------- 1 | import App from './App'; 2 | 3 | export default App; 4 | -------------------------------------------------------------------------------- /client/components/clickable/Clickable.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import noop from 'utils/noop'; 4 | import styles from './clickable.scss'; 5 | 6 | const Clickable = ({ content, onClick }) => ( 7 |
8 | {content} 9 |
10 | ); 11 | 12 | Clickable.propTypes = { 13 | content: PropTypes.node, 14 | onClick: PropTypes.func 15 | }; 16 | 17 | Clickable.defaultProps = { 18 | content: '...', 19 | onClick: noop 20 | }; 21 | 22 | export default Clickable; 23 | -------------------------------------------------------------------------------- /client/components/clickable/Clickable.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { shallow } from 'enzyme'; 4 | 5 | import Clickable from './Clickable'; 6 | 7 | describe('', () => { 8 | it('snapshot - default props', () => { 9 | const tree = renderer.create().toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | 13 | it('snapshot - with TEXT', () => { 14 | const tree = renderer.create().toJSON(); 15 | expect(tree).toMatchSnapshot(); 16 | }); 17 | }); 18 | 19 | describe('Clickable - on Click', () => { 20 | it('should call onClick callback', () => { 21 | const onClickCb = jest.fn(); 22 | 23 | const app = shallow(); 24 | 25 | app.simulate('click'); 26 | 27 | expect(onClickCb).toBeCalled(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /client/components/clickable/__snapshots__/Clickable.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot - default props 1`] = ` 4 |
8 | ... 9 |
10 | `; 11 | 12 | exports[` snapshot - with TEXT 1`] = ` 13 |
17 | TEXT 18 |
19 | `; 20 | -------------------------------------------------------------------------------- /client/components/clickable/clickable.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | 3 | .container { 4 | padding: 0.2rem; 5 | border: 0.1px solid $black; 6 | background: $gold; 7 | color: $black; 8 | cursor: pointer; 9 | transition: all .4s cubic-bezier(0.36, -1, 1, 2.65); 10 | 11 | &:hover { 12 | transform: scale(1.05, 1.15); 13 | font-weight: 600; 14 | background: $black; 15 | color: $gold; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/components/clickable/index.js: -------------------------------------------------------------------------------- 1 | import Clickable from './Clickable'; 2 | 3 | export default Clickable; 4 | -------------------------------------------------------------------------------- /client/components/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './counter.scss'; 3 | 4 | const leap = step => ({ value }) => ({ value: value + step }); 5 | 6 | class Counter extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.handleIncrement = this.handleIncrement.bind(this); 11 | this.handleDecrement = this.handleDecrement.bind(this); 12 | this.state = { value: 0 }; 13 | } 14 | 15 | handleIncrement() { 16 | this.setState(leap(1)); 17 | } 18 | 19 | handleDecrement() { 20 | this.setState(leap(-1)); 21 | } 22 | 23 | render() { 24 | return ( 25 |
26 |
{this.state.value}
27 |
28 | 29 | + 30 |
31 |
32 | ); 33 | } 34 | } 35 | 36 | export default Counter; 37 | -------------------------------------------------------------------------------- /client/components/counter/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import { shallow } from 'enzyme'; 4 | 5 | import Counter from './Counter'; 6 | 7 | describe('', () => { 8 | it('snapshot', () => { 9 | const renderer = new ShallowRenderer(); 10 | const tree = renderer.render(); 11 | 12 | expect(tree).toMatchSnapshot(); 13 | }); 14 | }); 15 | 16 | describe('Counter', () => { 17 | let tree; 18 | beforeEach(() => { 19 | tree = shallow(); 20 | }); 21 | 22 | it('should start with value in zero', () => { 23 | expect(tree.state()).toEqual({ value: 0 }); 24 | }); 25 | 26 | it('should decrement value', () => { 27 | const minus = tree.find('a').first(); 28 | minus.simulate('click'); 29 | 30 | expect(tree.state()).toEqual({ value: -1 }); 31 | }); 32 | 33 | it('should increment value', () => { 34 | const plus = tree.find('a').last(); 35 | plus.simulate('click'); 36 | 37 | expect(tree.state()).toEqual({ value: 1 }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /client/components/counter/__snapshots__/Counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 |
7 |
10 | 0 11 |
12 | 26 |
27 | `; 28 | -------------------------------------------------------------------------------- /client/components/counter/counter.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_mixins'; 2 | @import '~styles/_palette'; 3 | 4 | .container { 5 | background: $gold; 6 | border: 2px solid $black-2; 7 | @include box-shadow(1px, 1px, 4px,$slate); 8 | width: 240px; 9 | margin: 8px auto; 10 | position: relative; 11 | 12 | .value { 13 | cursor: default; 14 | font-size: 8rem; 15 | text-align: center; 16 | user-select: none; 17 | overflow: auto; 18 | } 19 | 20 | .controls { 21 | display: flex; 22 | justify-content: space-around; 23 | padding: 8px 24px; 24 | 25 | a { 26 | user-select: none; 27 | font-weight: bold; 28 | font-size: 3rem; 29 | cursor: pointer; 30 | @include box-shadow(1px, 1px, 4px,$slate); 31 | border-radius: 4px; 32 | width: 56px; 33 | background: $black-2; 34 | color: $gold; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/components/counter/index.js: -------------------------------------------------------------------------------- 1 | import Counter from './Counter'; 2 | 3 | export default Counter; 4 | -------------------------------------------------------------------------------- /client/components/nav/Nav.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import links from '../../routes/links'; 4 | import styles from './nav.scss'; 5 | 6 | const Nav = () => ( 7 |
    8 |
  • 9 | 10 | Home 11 | 12 |
  • 13 |
  • 14 | 15 | About 16 | 17 |
  • 18 |
19 | ); 20 | 21 | export default Nav; 22 | -------------------------------------------------------------------------------- /client/components/nav/Nav.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import Nav from './Nav'; 5 | 6 | describe('