├── README.md ├── chapter-03-mvp ├── .babelrc ├── .eslintrc ├── .gitignore ├── index.js ├── package.json ├── public │ ├── css │ │ ├── normalize.css │ │ └── resets.css │ └── index.html ├── source │ ├── components │ │ ├── App │ │ │ ├── index.jsx │ │ │ └── style.js │ │ ├── NoteDetail │ │ │ ├── index.jsx │ │ │ └── style.js │ │ └── NotesList │ │ │ ├── index.jsx │ │ │ └── style.js │ ├── index.js │ └── store │ │ ├── actions.js │ │ ├── actions.test.js │ │ ├── index.js │ │ ├── reducers.js │ │ ├── reducers.test.js │ │ ├── selectors.js │ │ ├── selectors.test.js │ │ └── testUtils.js └── webpack.config.js ├── chapter-04-syncing-with-server ├── .babelrc ├── .eslintrc ├── .gitignore ├── index.js ├── package.json ├── public │ ├── css │ │ ├── normalize.css │ │ └── resets.css │ └── index.html ├── source │ ├── components │ │ ├── App │ │ │ ├── index.jsx │ │ │ └── style.js │ │ ├── NoteDetail │ │ │ ├── index.jsx │ │ │ └── style.js │ │ └── NotesList │ │ │ ├── index.jsx │ │ │ └── style.js │ ├── index.js │ ├── store │ │ ├── actionUtils.js │ │ ├── actions.js │ │ ├── actions.test.js │ │ ├── index.js │ │ ├── reducers.js │ │ ├── reducers.test.js │ │ ├── selectors.js │ │ ├── selectors.test.js │ │ └── testUtils.js │ └── utils │ │ ├── api.js │ │ ├── api.test.js │ │ └── testUtils.js └── webpack.config.js └── chapter-05-request-state ├── .babelrc ├── .eslintrc ├── .gitignore ├── index.js ├── package.json ├── public ├── css │ ├── animations.css │ ├── normalize.css │ └── resets.css └── index.html ├── source ├── api │ ├── index.js │ ├── test.js │ └── testUtils.js ├── components │ ├── App │ │ ├── index.jsx │ │ └── style.js │ ├── Nav │ │ ├── index.jsx │ │ └── styles.js │ ├── NoteDetail │ │ ├── index.jsx │ │ └── style.js │ ├── NotesList │ │ ├── index.jsx │ │ └── style.js │ └── Toasts │ │ ├── index.jsx │ │ └── styles.js ├── index.js └── store │ ├── actions.js │ ├── actions.test.js │ ├── index.js │ ├── reducers.js │ ├── reducers.test.js │ ├── selectors.js │ ├── selectors.test.js │ └── testUtils.js └── webpack.config.js /README.md: -------------------------------------------------------------------------------- 1 | # Developing a Redux Edge 2 | 3 | This repo holds supplementary material for the **Developing a Redux Edge** book. 4 | -------------------------------------------------------------------------------- /chapter-03-mvp/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "stage-2" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /chapter-03-mvp/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "ecmaFeatures": { 9 | "jsx": true, 10 | "modules": true, 11 | "spread": true, 12 | "experimentalObjectRestSpread": true 13 | }, 14 | "plugins": [ 15 | "react" 16 | ], 17 | "rules": { 18 | "array-bracket-spacing": ["error", "always"], 19 | "block-spacing": "error", 20 | "comma-spacing": ["error", {"before": false, "after": true}], 21 | "eqeqeq": ["error", "smart"], 22 | "no-cond-assign": "error", 23 | "no-const-assign": "error", 24 | "no-dupe-args": "error", 25 | "no-dupe-class-members": "error", 26 | "no-dupe-keys": "error", 27 | "no-duplicate-case": "error", 28 | "no-empty": "error", 29 | "no-eval": "error", 30 | "no-extend-native": "error", 31 | "no-extra-semi": "error", 32 | "no-func-assign": "error", 33 | "no-invalid-this": "error", 34 | "no-irregular-whitespace": "error", 35 | "no-lone-blocks": "error", 36 | "no-mixed-spaces-and-tabs": "error", 37 | "no-sparse-arrays": "error", 38 | "no-undef": "error", 39 | "no-use-before-define": "error", 40 | "no-var": "error", 41 | "object-shorthand": "error", 42 | "prefer-arrow-callback": "error", 43 | "react/jsx-indent-props": ["error", 2], 44 | "react/jsx-no-duplicate-props": "error", 45 | "react/jsx-no-undef": "error", 46 | "react/jsx-uses-react": "error", 47 | "react/jsx-uses-vars": "error", 48 | "react/no-direct-mutation-state": "error", 49 | "react/no-unknown-property": "error", 50 | "react/self-closing-comp": "error", 51 | "react/wrap-multilines": "error", 52 | "arrow-parens": ["warn", "always"], 53 | "arrow-spacing": "warn", 54 | "brace-style": ["warn", "1tbs"], 55 | "comma-dangle": ["warn", "always-multiline"], 56 | "curly": ["warn", "multi-line"], 57 | "dot-location": ["warn", "property"], 58 | "jsx-quotes": ["warn", "prefer-double"], 59 | "no-console": "warn", 60 | "no-constant-condition": "warn", 61 | "no-fallthrough": "warn", 62 | "no-labels": "warn", 63 | "no-multiple-empty-lines": ["warn", {"max": 1}], 64 | "no-trailing-spaces": "warn", 65 | "no-underscore-dangle": "warn", 66 | "no-unneeded-ternary": "warn", 67 | "no-unused-vars": ["warn", {"varsIgnorePattern": "^styles$"}], 68 | "object-curly-spacing": ["warn", "always"], 69 | "prefer-const": "warn", 70 | "prefer-spread": "warn", 71 | "prefer-template": "warn", 72 | "react/jsx-closing-bracket-location": ["warn", {"selfClosing": "props-aligned", "nonEmpty": "props-aligned"}], 73 | "react/jsx-curly-spacing": ["warn", "never"], 74 | "react/no-danger": "warn", 75 | "react/prop-types": "warn", 76 | "semi": ["warn", "always"] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /chapter-03-mvp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/js 3 | -------------------------------------------------------------------------------- /chapter-03-mvp/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('*', (req, res) => { 8 | res.sendFile(path.resolve(__dirname, 'public/index.html')); 9 | }); 10 | 11 | app.listen(3000, () => { 12 | //eslint-disable-next-line no-console 13 | console.log('`Developing a Redux Edge` notes app listening on port 3000!'); 14 | }); 15 | -------------------------------------------------------------------------------- /chapter-03-mvp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dare-notes-app", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "lint": "eslint 'source/**'", 7 | "test": "tape --require babel-register source/**/*.test.js", 8 | "prestart": "webpack", 9 | "start": "node index" 10 | }, 11 | "author": "Artur Muller (http://arturmuller.com)", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "babel-eslint": "^6.0.0", 15 | "babel-register": "^6.9.0", 16 | "eslint": "^2.12.0", 17 | "eslint-plugin-react": "^4.3.0", 18 | "tape": "^4.5.1" 19 | }, 20 | "dependencies": { 21 | "babel-core": "^6.7.6", 22 | "babel-loader": "^6.2.4", 23 | "babel-preset-es2015": "^6.6.0", 24 | "babel-preset-react": "^6.5.0", 25 | "babel-preset-stage-2": "^6.5.0", 26 | "express": "^4.13.4", 27 | "ramda": "^0.21.0", 28 | "react": "^15.0.0", 29 | "react-dom": "^15.0.0", 30 | "react-redux": "^4.0.0", 31 | "redux": "^3.0.0", 32 | "uuid": "^2.0.2", 33 | "webpack": "^1.12.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chapter-03-mvp/public/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 6 | */ 7 | 8 | html { 9 | font-family: sans-serif; /* 1 */ 10 | -ms-text-size-adjust: 100%; /* 2 */ 11 | -webkit-text-size-adjust: 100%; /* 2 */ 12 | } 13 | 14 | /** 15 | * Remove the margin in all browsers (opinionated). 16 | */ 17 | 18 | body { 19 | margin: 0; 20 | } 21 | 22 | /* HTML5 display definitions 23 | ========================================================================== */ 24 | 25 | /** 26 | * Add the correct display in IE 9-. 27 | * 1. Add the correct display in Edge, IE, and Firefox. 28 | * 2. Add the correct display in IE. 29 | */ 30 | 31 | article, 32 | aside, 33 | details, /* 1 */ 34 | figcaption, 35 | figure, 36 | footer, 37 | header, 38 | main, /* 2 */ 39 | menu, 40 | nav, 41 | section, 42 | summary { /* 1 */ 43 | display: block; 44 | } 45 | 46 | /** 47 | * Add the correct display in IE 9-. 48 | */ 49 | 50 | audio, 51 | canvas, 52 | progress, 53 | video { 54 | display: inline-block; 55 | } 56 | 57 | /** 58 | * Add the correct display in iOS 4-7. 59 | */ 60 | 61 | audio:not([controls]) { 62 | display: none; 63 | height: 0; 64 | } 65 | 66 | /** 67 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 68 | */ 69 | 70 | progress { 71 | vertical-align: baseline; 72 | } 73 | 74 | /** 75 | * Add the correct display in IE 10-. 76 | * 1. Add the correct display in IE. 77 | */ 78 | 79 | template, /* 1 */ 80 | [hidden] { 81 | display: none; 82 | } 83 | 84 | /* Links 85 | ========================================================================== */ 86 | 87 | /** 88 | * 1. Remove the gray background on active links in IE 10. 89 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 90 | */ 91 | 92 | a { 93 | background-color: transparent; /* 1 */ 94 | -webkit-text-decoration-skip: objects; /* 2 */ 95 | } 96 | 97 | /** 98 | * Remove the outline on focused links when they are also active or hovered 99 | * in all browsers (opinionated). 100 | */ 101 | 102 | a:active, 103 | a:hover { 104 | outline-width: 0; 105 | } 106 | 107 | /* Text-level semantics 108 | ========================================================================== */ 109 | 110 | /** 111 | * 1. Remove the bottom border in Firefox 39-. 112 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: none; /* 1 */ 117 | text-decoration: underline; /* 2 */ 118 | text-decoration: underline dotted; /* 2 */ 119 | } 120 | 121 | /** 122 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 123 | */ 124 | 125 | b, 126 | strong { 127 | font-weight: inherit; 128 | } 129 | 130 | /** 131 | * Add the correct font weight in Chrome, Edge, and Safari. 132 | */ 133 | 134 | b, 135 | strong { 136 | font-weight: bolder; 137 | } 138 | 139 | /** 140 | * Add the correct font style in Android 4.3-. 141 | */ 142 | 143 | dfn { 144 | font-style: italic; 145 | } 146 | 147 | /** 148 | * Correct the font size and margin on `h1` elements within `section` and 149 | * `article` contexts in Chrome, Firefox, and Safari. 150 | */ 151 | 152 | h1 { 153 | font-size: 2em; 154 | margin: 0.67em 0; 155 | } 156 | 157 | /** 158 | * Add the correct background and color in IE 9-. 159 | */ 160 | 161 | mark { 162 | background-color: #ff0; 163 | color: #000; 164 | } 165 | 166 | /** 167 | * Add the correct font size in all browsers. 168 | */ 169 | 170 | small { 171 | font-size: 80%; 172 | } 173 | 174 | /** 175 | * Prevent `sub` and `sup` elements from affecting the line height in 176 | * all browsers. 177 | */ 178 | 179 | sub, 180 | sup { 181 | font-size: 75%; 182 | line-height: 0; 183 | position: relative; 184 | vertical-align: baseline; 185 | } 186 | 187 | sub { 188 | bottom: -0.25em; 189 | } 190 | 191 | sup { 192 | top: -0.5em; 193 | } 194 | 195 | /* Embedded content 196 | ========================================================================== */ 197 | 198 | /** 199 | * Remove the border on images inside links in IE 10-. 200 | */ 201 | 202 | img { 203 | border-style: none; 204 | } 205 | 206 | /** 207 | * Hide the overflow in IE. 208 | */ 209 | 210 | svg:not(:root) { 211 | overflow: hidden; 212 | } 213 | 214 | /* Grouping content 215 | ========================================================================== */ 216 | 217 | /** 218 | * 1. Correct the inheritance and scaling of font size in all browsers. 219 | * 2. Correct the odd `em` font sizing in all browsers. 220 | */ 221 | 222 | code, 223 | kbd, 224 | pre, 225 | samp { 226 | font-family: monospace, monospace; /* 1 */ 227 | font-size: 1em; /* 2 */ 228 | } 229 | 230 | /** 231 | * Add the correct margin in IE 8. 232 | */ 233 | 234 | figure { 235 | margin: 1em 40px; 236 | } 237 | 238 | /** 239 | * 1. Add the correct box sizing in Firefox. 240 | * 2. Show the overflow in Edge and IE. 241 | */ 242 | 243 | hr { 244 | box-sizing: content-box; /* 1 */ 245 | height: 0; /* 1 */ 246 | overflow: visible; /* 2 */ 247 | } 248 | 249 | /* Forms 250 | ========================================================================== */ 251 | 252 | /** 253 | * 1. Change font properties to `inherit` in all browsers (opinionated). 254 | * 2. Remove the margin in Firefox and Safari. 255 | */ 256 | 257 | button, 258 | input, 259 | select, 260 | textarea { 261 | font: inherit; /* 1 */ 262 | margin: 0; /* 2 */ 263 | } 264 | 265 | /** 266 | * Restore the font weight unset by the previous rule. 267 | */ 268 | 269 | optgroup { 270 | font-weight: bold; 271 | } 272 | 273 | /** 274 | * Show the overflow in IE. 275 | * 1. Show the overflow in Edge. 276 | */ 277 | 278 | button, 279 | input { /* 1 */ 280 | overflow: visible; 281 | } 282 | 283 | /** 284 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 285 | * 1. Remove the inheritance of text transform in Firefox. 286 | */ 287 | 288 | button, 289 | select { /* 1 */ 290 | text-transform: none; 291 | } 292 | 293 | /** 294 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 295 | * controls in Android 4. 296 | * 2. Correct the inability to style clickable types in iOS and Safari. 297 | */ 298 | 299 | button, 300 | html [type="button"], /* 1 */ 301 | [type="reset"], 302 | [type="submit"] { 303 | -webkit-appearance: button; /* 2 */ 304 | } 305 | 306 | /** 307 | * Remove the inner border and padding in Firefox. 308 | */ 309 | 310 | button::-moz-focus-inner, 311 | [type="button"]::-moz-focus-inner, 312 | [type="reset"]::-moz-focus-inner, 313 | [type="submit"]::-moz-focus-inner { 314 | border-style: none; 315 | padding: 0; 316 | } 317 | 318 | /** 319 | * Restore the focus styles unset by the previous rule. 320 | */ 321 | 322 | button:-moz-focusring, 323 | [type="button"]:-moz-focusring, 324 | [type="reset"]:-moz-focusring, 325 | [type="submit"]:-moz-focusring { 326 | outline: 1px dotted ButtonText; 327 | } 328 | 329 | /** 330 | * Change the border, margin, and padding in all browsers (opinionated). 331 | */ 332 | 333 | fieldset { 334 | border: 1px solid #c0c0c0; 335 | margin: 0 2px; 336 | padding: 0.35em 0.625em 0.75em; 337 | } 338 | 339 | /** 340 | * 1. Correct the text wrapping in Edge and IE. 341 | * 2. Correct the color inheritance from `fieldset` elements in IE. 342 | * 3. Remove the padding so developers are not caught out when they zero out 343 | * `fieldset` elements in all browsers. 344 | */ 345 | 346 | legend { 347 | box-sizing: border-box; /* 1 */ 348 | color: inherit; /* 2 */ 349 | display: table; /* 1 */ 350 | max-width: 100%; /* 1 */ 351 | padding: 0; /* 3 */ 352 | white-space: normal; /* 1 */ 353 | } 354 | 355 | /** 356 | * Remove the default vertical scrollbar in IE. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; 361 | } 362 | 363 | /** 364 | * 1. Add the correct box sizing in IE 10-. 365 | * 2. Remove the padding in IE 10-. 366 | */ 367 | 368 | [type="checkbox"], 369 | [type="radio"] { 370 | box-sizing: border-box; /* 1 */ 371 | padding: 0; /* 2 */ 372 | } 373 | 374 | /** 375 | * Correct the cursor style of increment and decrement buttons in Chrome. 376 | */ 377 | 378 | [type="number"]::-webkit-inner-spin-button, 379 | [type="number"]::-webkit-outer-spin-button { 380 | height: auto; 381 | } 382 | 383 | /** 384 | * 1. Correct the odd appearance in Chrome and Safari. 385 | * 2. Correct the outline style in Safari. 386 | */ 387 | 388 | [type="search"] { 389 | -webkit-appearance: textfield; /* 1 */ 390 | outline-offset: -2px; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 395 | */ 396 | 397 | [type="search"]::-webkit-search-cancel-button, 398 | [type="search"]::-webkit-search-decoration { 399 | -webkit-appearance: none; 400 | } 401 | 402 | /** 403 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 404 | */ 405 | 406 | ::-webkit-input-placeholder { 407 | color: inherit; 408 | opacity: 0.54; 409 | } 410 | 411 | /** 412 | * 1. Correct the inability to style clickable types in iOS and Safari. 413 | * 2. Change font properties to `inherit` in Safari. 414 | */ 415 | 416 | ::-webkit-file-upload-button { 417 | -webkit-appearance: button; /* 1 */ 418 | font: inherit; /* 2 */ 419 | } 420 | -------------------------------------------------------------------------------- /chapter-03-mvp/public/css/resets.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | font-family: Verdana, sans-serif; 7 | font-size: 15px; 8 | } 9 | 10 | h1, h2, h3, h4, h5, h6, p, button, textarea { 11 | margin: 0; 12 | padding: 0; 13 | } 14 | 15 | a, button, textarea { 16 | display: block; 17 | outline: none; 18 | } 19 | 20 | button, textarea { 21 | border: none; 22 | background-color: transparent; 23 | } 24 | 25 | textarea { 26 | width: 100%; 27 | resize: none; 28 | } 29 | -------------------------------------------------------------------------------- /chapter-03-mvp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Initial | Developing a Redux Edge 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /chapter-03-mvp/source/components/App/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import NotesList from '../NotesList'; 3 | import NoteDetail from '../NoteDetail'; 4 | import * as style from './style'; 5 | 6 | const App = () => ( 7 |
8 | 9 | 10 |
11 | ); 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /chapter-03-mvp/source/components/App/style.js: -------------------------------------------------------------------------------- 1 | export const wrapper = { 2 | display: 'flex', 3 | alignItems: 'center', 4 | }; 5 | -------------------------------------------------------------------------------- /chapter-03-mvp/source/components/NoteDetail/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import * as actionCreators from '../../store/actions'; 4 | import * as selectors from '../../store/selectors'; 5 | import * as style from './style'; 6 | 7 | const NoteDetail = ({ note, removeNote, closeNote, updateNote }) => ( 8 |
9 | {!note 10 | ?
No note is open
11 | :
12 |
13 | {new Date(note.timestamp).toLocaleString()} 14 |
15 |