├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTIONS.md
├── LICENSE
├── README.md
├── code
├── README.md
├── chapter-10
│ ├── .gitignore
│ ├── .storybook
│ │ └── config.js
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── App.js
│ │ │ │ ├── App.spec.js
│ │ │ │ └── App.styles.js
│ │ │ ├── TodosList
│ │ │ │ ├── TodosList.js
│ │ │ │ ├── TodosList.spec.js
│ │ │ │ ├── TodosList.story.js
│ │ │ │ └── TodosList.styles.js
│ │ │ ├── TodosListInfo
│ │ │ │ ├── TodosListInfo.js
│ │ │ │ ├── TodosListInfo.spec.js
│ │ │ │ ├── TodosListInfo.story.js
│ │ │ │ └── TodosListInfo.styles.js
│ │ │ └── TodosListItem
│ │ │ │ ├── TodosListItem.js
│ │ │ │ ├── TodosListItem.spec.js
│ │ │ │ ├── TodosListItem.story.js
│ │ │ │ └── TodosListItem.styles.js
│ │ ├── index.js
│ │ └── styles
│ │ │ ├── globals.styles.js
│ │ │ ├── mixins.js
│ │ │ └── theme.js
│ ├── stories
│ │ ├── Button.js
│ │ ├── Welcome.js
│ │ └── index.js
│ └── yarn.lock
├── chapter-3
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ └── index.js
│ └── yarn.lock
├── chapter-4
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── index.js
│ │ └── styles.css
│ └── yarn.lock
├── chapter-5
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── App.css
│ │ │ │ ├── App.js
│ │ │ │ └── App.spec.js
│ │ │ ├── TodosList
│ │ │ │ ├── TodosList.css
│ │ │ │ ├── TodosList.js
│ │ │ │ └── TodosList.spec.js
│ │ │ └── TodosListItem
│ │ │ │ ├── TodosListItem.css
│ │ │ │ ├── TodosListItem.js
│ │ │ │ └── TodosListItem.spec.js
│ │ ├── index.js
│ │ └── styles.css
│ └── yarn.lock
├── chapter-6
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── App.js
│ │ │ │ ├── App.scss
│ │ │ │ └── App.spec.js
│ │ │ ├── TodosList
│ │ │ │ ├── TodosList.js
│ │ │ │ ├── TodosList.scss
│ │ │ │ └── TodosList.spec.js
│ │ │ ├── TodosListInfo
│ │ │ │ ├── TodosListInfo.js
│ │ │ │ ├── TodosListInfo.scss
│ │ │ │ └── TodosListInfo.spec.js
│ │ │ └── TodosListItem
│ │ │ │ ├── TodosListItem.js
│ │ │ │ ├── TodosListItem.scss
│ │ │ │ └── TodosListItem.spec.js
│ │ ├── index.js
│ │ └── styles
│ │ │ ├── globals.scss
│ │ │ ├── main.scss
│ │ │ └── theme.scss
│ └── yarn.lock
├── chapter-7
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── App.js
│ │ │ │ ├── App.scss
│ │ │ │ └── App.spec.js
│ │ │ ├── TodosList
│ │ │ │ ├── TodosList.js
│ │ │ │ ├── TodosList.scss
│ │ │ │ └── TodosList.spec.js
│ │ │ ├── TodosListInfo
│ │ │ │ ├── TodosListInfo.js
│ │ │ │ ├── TodosListInfo.scss
│ │ │ │ └── TodosListInfo.spec.js
│ │ │ └── TodosListItem
│ │ │ │ ├── TodosListItem.js
│ │ │ │ ├── TodosListItem.scss
│ │ │ │ └── TodosListItem.spec.js
│ │ ├── index.js
│ │ └── styles
│ │ │ ├── globals.scss
│ │ │ ├── main.scss
│ │ │ └── theme.scss
│ └── yarn.lock
├── chapter-8
│ ├── .gitignore
│ ├── config
│ │ ├── env.js
│ │ ├── jest
│ │ │ ├── cssTransform.js
│ │ │ └── fileTransform.js
│ │ ├── paths.js
│ │ ├── polyfills.js
│ │ ├── webpack.config.dev.js
│ │ ├── webpack.config.prod.js
│ │ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── scripts
│ │ ├── build.js
│ │ ├── start.js
│ │ └── test.js
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── App.js
│ │ │ │ ├── App.spec.js
│ │ │ │ └── App.styles.js
│ │ │ ├── TodosList
│ │ │ │ ├── TodosList.js
│ │ │ │ ├── TodosList.spec.js
│ │ │ │ └── TodosList.styles.js
│ │ │ ├── TodosListInfo
│ │ │ │ ├── TodosListInfo.js
│ │ │ │ ├── TodosListInfo.spec.js
│ │ │ │ └── TodosListInfo.styles.js
│ │ │ └── TodosListItem
│ │ │ │ ├── TodosListItem.js
│ │ │ │ ├── TodosListItem.spec.js
│ │ │ │ └── TodosListItem.styles.js
│ │ ├── index.js
│ │ └── styles
│ │ │ ├── globals.styles.js
│ │ │ └── theme.js
│ └── yarn.lock
└── chapter-9
│ ├── .gitignore
│ ├── config
│ ├── env.js
│ ├── jest
│ │ ├── cssTransform.js
│ │ └── fileTransform.js
│ ├── paths.js
│ ├── polyfills.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── webpackDevServer.config.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── scripts
│ ├── build.js
│ ├── start.js
│ └── test.js
│ ├── src
│ ├── components
│ │ ├── App
│ │ │ ├── App.js
│ │ │ ├── App.spec.js
│ │ │ └── App.styles.js
│ │ ├── TodosList
│ │ │ ├── TodosList.js
│ │ │ ├── TodosList.spec.js
│ │ │ └── TodosList.styles.js
│ │ ├── TodosListInfo
│ │ │ ├── TodosListInfo.js
│ │ │ ├── TodosListInfo.spec.js
│ │ │ └── TodosListInfo.styles.js
│ │ └── TodosListItem
│ │ │ ├── TodosListItem.js
│ │ │ ├── TodosListItem.spec.js
│ │ │ └── TodosListItem.styles.js
│ ├── index.js
│ └── styles
│ │ ├── globals.styles.js
│ │ ├── mixins.js
│ │ └── theme.js
│ └── yarn.lock
└── manuscript
├── Book.txt
├── chapter1.md
├── chapter10.md
├── chapter11.md
├── chapter2.md
├── chapter3.md
├── chapter4.md
├── chapter5.md
├── chapter6.md
├── chapter7.md
├── chapter8.md
├── chapter9.md
├── images
├── app-layout.jpg
├── app-layout2.jpg
├── babel.png
├── batman-important-css.jpeg
├── book-cover-long.jpg
├── book-cover-preview.jpg
├── book-cover.jpg
├── chedeau-commandments.jpeg
├── eslint.png
├── hello-world.png
├── jsx.png
├── starter.png
├── storybook.png
├── tasks-1.png
├── tasks-2.png
├── tasks-3.png
├── tasks-4.png
├── tasks-5.png
├── tasks-6.png
├── tasks-7.png
├── unidirectional-styles.jpg
└── webpack.png
├── preface.md
└── profile.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # testing
5 | /coverage
6 |
7 | # production
8 | /build
9 | /preview
10 | /export
11 | /published
12 |
13 | # misc
14 | .DS_Store
15 | .env
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 | .idea
20 | .dropbox
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at me@farhadg.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTIONS.md:
--------------------------------------------------------------------------------
1 | # Guidelines
2 | Thank you for your interest in contributing to this book.
3 |
4 | If you have any improvements, please fork this repository, make the necessary changes and open a [pull request](https://github.com/FarhadG/ui-react/pulls). Do keep in mind that all contributions are considered donations under [LICENSE](LICENSE) agreements.
5 |
6 | If you have any questions, feel free to contact me. Thank you!
7 |
8 | ## Contributors
9 | - [@farhadg](https://github.com/FarhadG)
10 | - [@jenscales](https://github.com/jenscales)
11 | - [@alexanderGugel](https://github.com/alexanderGugel)
12 | - [@innovmon](https://github.com/innovmon)
13 | - @averroes
14 |
--------------------------------------------------------------------------------
/code/chapter-10/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/code/chapter-10/.storybook/config.js:
--------------------------------------------------------------------------------
1 | // dependencies
2 | import React from 'react';
3 | import normalize from 'radium-normalize';
4 | import { Style, StyleRoot } from 'radium';
5 | import infoAddon from '@storybook/addon-info';
6 | import { ThemeWrapProvider } from 'theme-wrap';
7 | import { addDecorator, configure, setAddon } from '@kadira/storybook';
8 |
9 | // local dependencies
10 | import mixins from '../src/styles/mixins';
11 | import theme from '../src/styles/theme';
12 | import globalStyles from '../src/styles/globals.styles';
13 |
14 | const Root = story => (
15 |
16 |
17 |
18 |
20 | {story()}
21 |
22 |
23 | );
24 |
25 | // attach our decorator
26 | addDecorator(Root);
27 |
28 | // enable component info
29 | setAddon(infoAddon);
30 |
31 | configure(() => {
32 | require('../src/components/TodosList/TodosList.story');
33 | require('../src/components/TodosListInfo/TodosListInfo.story');
34 | require('../src/components/TodosListItem/TodosListItem.story');
35 | }, module);
--------------------------------------------------------------------------------
/code/chapter-10/config/env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 |
7 | // Make sure that including paths.js after env.js will read .env variables.
8 | delete require.cache[require.resolve('./paths')];
9 |
10 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | var dotenvFiles = [
19 | `${paths.dotenv}.${NODE_ENV}.local`,
20 | `${paths.dotenv}.${NODE_ENV}`,
21 | // Don't include `.env.local` for `test` environment
22 | // since normally you expect tests to produce the same
23 | // results for everyone
24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 | paths.dotenv,
26 | ].filter(Boolean);
27 |
28 | // Load environment variables from .env* files. Suppress warnings using silent
29 | // if this file is missing. dotenv will never modify any environment variables
30 | // that have already been set.
31 | // https://github.com/motdotla/dotenv
32 | dotenvFiles.forEach(dotenvFile => {
33 | if (fs.existsSync(dotenvFile)) {
34 | require('dotenv').config({
35 | path: dotenvFile,
36 | });
37 | }
38 | });
39 |
40 | // We support resolving modules according to `NODE_PATH`.
41 | // This lets you use absolute paths in imports inside large monorepos:
42 | // https://github.com/facebookincubator/create-react-app/issues/253.
43 | // It works similar to `NODE_PATH` in Node itself:
44 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
45 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
46 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
47 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
48 | // We also resolve them to make sure all tools using them work consistently.
49 | const appDirectory = fs.realpathSync(process.cwd());
50 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
51 | .split(path.delimiter)
52 | .filter(folder => folder && !path.isAbsolute(folder))
53 | .map(folder => path.resolve(appDirectory, folder))
54 | .join(path.delimiter);
55 |
56 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
57 | // injected into the application via DefinePlugin in Webpack configuration.
58 | const REACT_APP = /^REACT_APP_/i;
59 |
60 | function getClientEnvironment(publicUrl) {
61 | const raw = Object.keys(process.env)
62 | .filter(key => REACT_APP.test(key))
63 | .reduce(
64 | (env, key) => {
65 | env[key] = process.env[key];
66 | return env;
67 | },
68 | {
69 | // Useful for determining whether we’re running in production mode.
70 | // Most importantly, it switches React into the correct mode.
71 | NODE_ENV: process.env.NODE_ENV || 'development',
72 | // Useful for resolving the correct path to static assets in `public`.
73 | // For example,
.
74 | // This should only be used as an escape hatch. Normally you would put
75 | // images into the `src` and `import` them in code to get their paths.
76 | PUBLIC_URL: publicUrl,
77 | }
78 | );
79 | // Stringify all values so we can feed into Webpack DefinePlugin
80 | const stringified = {
81 | 'process.env': Object.keys(raw).reduce((env, key) => {
82 | env[key] = JSON.stringify(raw[key]);
83 | return env;
84 | }, {}),
85 | };
86 |
87 | return { raw, stringified };
88 | }
89 |
90 | module.exports = getClientEnvironment;
91 |
--------------------------------------------------------------------------------
/code/chapter-10/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/code/chapter-10/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/code/chapter-10/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right