├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .travis.yml
├── README.md
├── package.json
├── private
├── jest
│ ├── componentsMock.js
│ ├── fileMock.js
│ └── setupTests.js
└── storybook
│ ├── config.js
│ └── webpack.config.js
├── public
├── icon.png
├── index.html
└── thumbnail.png
├── src-example
├── components
│ ├── App.js
│ ├── atoms
│ │ ├── Atom
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Badge
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Block
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Button
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Caption
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Heading
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── HorizontalRule
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Icon
│ │ │ ├── icons
│ │ │ │ ├── arc.svg
│ │ │ │ ├── atomic-design.svg
│ │ │ │ ├── close.svg
│ │ │ │ ├── copy.svg
│ │ │ │ ├── dist.svg
│ │ │ │ ├── docs.svg
│ │ │ │ ├── github.svg
│ │ │ │ ├── heart.svg
│ │ │ │ ├── jest.svg
│ │ │ │ ├── react-router.svg
│ │ │ │ ├── react.svg
│ │ │ │ ├── redux.svg
│ │ │ │ └── webpack.svg
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Input
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Label
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Link
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── List
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── LogoImage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ ├── index.test.js
│ │ │ └── logo.svg
│ │ ├── Paragraph
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── PreformattedText
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Spinner
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── TableCell
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── TableRow
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ └── Tooltip
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ ├── index.js
│ ├── molecules
│ │ ├── Blockquote
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Feature
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Field
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── IconButton
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── IconLink
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Modal
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Molecule
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── PrimaryNavigation
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Slider
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ └── Table
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ ├── organisms
│ │ ├── FeatureList
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Footer
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Header
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── Hero
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ └── Organism
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ ├── pages
│ │ ├── GenericPage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── HomePage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ ├── NotFoundPage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ │ └── SamplePage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ ├── templates
│ │ ├── GenericTemplate
│ │ │ ├── index.js
│ │ │ └── index.test.js
│ │ └── PageTemplate
│ │ │ ├── index.js
│ │ │ └── index.test.js
│ └── themes
│ │ └── default.js
├── config.js
└── index.js
├── src
├── components
│ ├── App.js
│ ├── index.js
│ ├── pages
│ │ └── HomePage
│ │ │ ├── index.js
│ │ │ ├── index.stories.js
│ │ │ └── index.test.js
│ └── themes
│ │ └── default.js
├── config.js
└── index.js
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "modules": false
7 | }
8 | ],
9 | "@babel/preset-react"
10 | ],
11 | "plugins": [
12 | "react-hot-loader/babel",
13 | "@babel/plugin-syntax-dynamic-import",
14 | "@babel/plugin-syntax-import-meta",
15 | "@babel/plugin-proposal-class-properties",
16 | "@babel/plugin-proposal-json-strings",
17 | [
18 | "@babel/plugin-proposal-decorators",
19 | {
20 | "legacy": true
21 | }
22 | ],
23 | "@babel/plugin-proposal-function-sent",
24 | "@babel/plugin-proposal-export-namespace-from",
25 | "@babel/plugin-proposal-numeric-separator",
26 | "@babel/plugin-proposal-throw-expressions",
27 | "@babel/plugin-proposal-export-default-from",
28 | "@babel/plugin-proposal-logical-assignment-operators",
29 | "@babel/plugin-proposal-optional-chaining",
30 | [
31 | "@babel/plugin-proposal-pipeline-operator",
32 | {
33 | "proposal": "minimal"
34 | }
35 | ],
36 | "@babel/plugin-proposal-nullish-coalescing-operator",
37 | "@babel/plugin-proposal-do-expressions"
38 | ],
39 | "env": {
40 | "development": {
41 | "plugins": [
42 | "@babel/plugin-transform-modules-commonjs",
43 | [
44 | "styled-components",
45 | {
46 | "minify": false
47 | }
48 | ]
49 | ]
50 | },
51 | "test": {
52 | "plugins": [
53 | "@babel/plugin-transform-modules-commonjs"
54 | ]
55 | },
56 | "production": {
57 | "plugins": [
58 | "transform-react-remove-prop-types",
59 | [
60 | "styled-components",
61 | {
62 | "displayName": false
63 | }
64 | ]
65 | ]
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | # Change these settings to your own preference
10 | indent_style = space
11 | indent_size = 2
12 |
13 | # We recommend you to keep these unchanged
14 | end_of_line = lf
15 | charset = utf-8
16 | trim_trailing_whitespace = true
17 | insert_final_newline = true
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "airbnb"
5 | ],
6 | "env": {
7 | "browser": true,
8 | "jest": true
9 | },
10 | "globals": {
11 | "__DEV__": true,
12 | "__PROD__": true,
13 | "__DEBUG__": true,
14 | "__COVERAGE__": true,
15 | "__BASENAME__": true
16 | },
17 | "settings": {
18 | "import/resolver": {
19 | "webpack": {
20 | "config": "webpack.config.js"
21 | }
22 | }
23 | },
24 | "rules": {
25 | "semi": [2, "never"],
26 | "comma-dangle": [2, "always-multiline"],
27 | "max-len": 0,
28 | "no-shadow": 0,
29 | "arrow-body-style": 0,
30 | "global-require": 0,
31 | "no-unused-expressions": 0,
32 | "no-confusing-arrow": 0,
33 | "no-unused-vars": [2, { "ignoreRestSiblings": true }],
34 | "import/no-dynamic-require": 0,
35 | "import/no-extraneous-dependencies": 0,
36 | "import/prefer-default-export": 0,
37 | "react/require-default-props": 0,
38 | "react/forbid-prop-types": 0,
39 | "react/default-props-match-prop-types": 0,
40 | "react/jsx-filename-extension": [2, {"extensions": [".js", ".jsx"]}],
41 | "jsx-a11y/anchor-is-valid": [2, {
42 | "components": ["Link"],
43 | "specialLink": ["hrefLeft", "hrefRight", "to"],
44 | "aspects": ["noHref", "invalidHref", "preferButton"]
45 | }]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | yarn.lock merge=ours
2 | package-lock.json merge=ours
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .happypack
3 | dist
4 | *.log
5 | node_modules
6 | coverage
7 | webpack/webpack-assets.json
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 8
4 | script:
5 | - npm run lint
6 | - npm test -- --coverage
7 | - npm run build
8 | cache:
9 | - yarn
10 | after_success:
11 | - bash <(curl -s https://codecov.io/bash)
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | **ARc** (Atomic React) is a React starter kit based on the [Atomic Design](http://bradfrost.com/blog/post/atomic-web-design/) methodology. It's progressive, which means that you can start with the basic boilerplate and try the other features when you are comfortable.
9 |
10 | - **[Demo](https://arc.js.org)**
11 | - **[Documentation](https://github.com/diegohaz/arc/wiki)**
12 |
13 |
14 |
15 |
16 | If you find this useful, please check out Reakit , a toolkit for building composable UI with React .
17 |
18 |
19 |
20 |
21 | ## Branches
22 |
23 | - ### [`master`](https://github.com/diegohaz/arc)
24 |
25 | The basic stack with [React](https://facebook.github.io/react/), [Webpack](https://github.com/webpack/webpack), [react-router](https://github.com/ReactTraining/react-router) and [Jest](https://facebook.github.io/jest/).
26 |
27 | - ### [`redux`](https://github.com/diegohaz/arc/tree/redux) ([compare](https://github.com/diegohaz/arc/compare/master...redux?diff=split#files_bucket))
28 |
29 | Master plus [redux](https://github.com/reactjs/redux), [redux-saga](https://github.com/yelouafi/redux-saga) and [redux-form](https://github.com/erikras/redux-form).
30 |
31 | - ### [`redux-ssr`](https://github.com/diegohaz/arc/tree/redux-ssr) ([compare](https://github.com/diegohaz/arc/compare/redux...redux-ssr?diff=split#files_bucket))
32 |
33 | Redux plus [Server Side Rendering](https://github.com/reactjs/redux/blob/master/docs/recipes/ServerRendering.md)
34 |
35 | ## Why
36 |
37 | I've been a web developer for the past 14 years and after dealing with IE vs. Netscape wars, `` layouts and flash websites, I can say that we are now living in the best moment in web development. Web components are awesome and React makes it better.
38 |
39 | React encourages you to create very small and pure components. However, as your project grows, you will have an increasingly complex components folder. At some point, this will be really huge and hard to maintain.
40 |
41 | I had a React project with more than 100 components in the `components` folder. The first approach I tried to organize it was separating the components by domain (described [here](http://marmelab.com/blog/2015/12/17/react-directory-structure.html)), but I realized that most of my components didn't belong to any domain, but were shared. This meant that my problems just moved to the `commons` folder.
42 |
43 | The [Atomic Design](http://bradfrost.com/blog/post/atomic-web-design/) approach comes handy to solve this problem because it considers the reusability through composition, *which is actually what React is*. You will have your minimal/stylish components in one folder, pages in another and so on.
44 |
45 | ## Setup
46 |
47 | ### 1. Get the source code
48 |
49 | Just clone one of the ARc [branches](#branches):
50 | ```sh
51 | $ git clone -b master https://github.com/diegohaz/arc my-app
52 | $ cd my-app
53 | ```
54 |
55 | You will probably want to remove ARc git history and start a brand new repository:
56 | ```sh
57 | $ rm -rf .git
58 | $ git init
59 | ```
60 |
61 | ### 2. Install dependencies
62 |
63 | ```sh
64 | $ npm install
65 | ```
66 |
67 | ### 3. Run the app
68 |
69 | ```sh
70 | $ npm run dev
71 | ```
72 |
73 | It will start the development server with [HMR](https://webpack.github.io/docs/hot-module-replacement) on top of it.
74 |
75 | > [http://localhost:3000](http://localhost:3000) — Development server
76 | > [http://localhost:3001](http://localhost:3001) — Webpack assets server (for `redux-ssr` only)
77 |
78 | Now you can open [http://localhost:3000](http://localhost:3000) in browser and start developing.
79 |
80 | ## Contributing
81 |
82 | When submitting an issue, use the following patterns in the title for better understanding:
83 | ```bash
84 | [v0.3.1-redux] Something wrong is not right # the v0.3.1 release of the redux branch
85 | [redux] Something wrong is not right # the actual code of the redux branch
86 | Something wrong is right # general, related to master or not directly related to any branch
87 | ```
88 |
89 | PRs are very appreciated. For bugs/features consider creating an issue before sending a PR.
90 |
91 | ## License
92 |
93 | MIT © [Diego Haz](https://github.com/diegohaz)
94 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arc",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "npm run dev",
7 | "env:dev": "cross-env NODE_ENV=development",
8 | "env:prod": "cross-env NODE_ENV=production",
9 | "env:example": "cross-env SOURCE=src-example",
10 | "test": "jest",
11 | "coverage": "npm test -- --coverage",
12 | "postcoverage": "opn coverage/lcov-report/index.html",
13 | "lint": "eslint src src-example private webpack.config.js",
14 | "storybook": "start-storybook -p 9001 -c private/storybook",
15 | "storybook:example": "npm run env:example -- npm run storybook",
16 | "dev": "npm run env:dev -- webpack-dev-server",
17 | "dev:example": "npm run env:example -- npm run dev",
18 | "build:clean": "rimraf \"dist/!(.git*|Procfile)**\"",
19 | "build:copy": "copyfiles -u 1 public/* public/**/* dist",
20 | "prebuild": "npm run build:clean && npm run build:copy",
21 | "build": "npm run env:prod -- webpack",
22 | "build:example": "npm run env:example -- npm run build"
23 | },
24 | "jest": {
25 | "moduleDirectories": [
26 | "src",
27 | "src-example",
28 | "node_modules"
29 | ],
30 | "moduleNameMapper": {
31 | "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/private/jest/fileMock.js",
32 | "^components$": "/private/jest/componentsMock.js"
33 | },
34 | "setupFiles": [
35 | "/private/jest/setupTests.js"
36 | ]
37 | },
38 | "devDependencies": {
39 | "@babel/core": "^7.0.0",
40 | "@babel/plugin-proposal-class-properties": "^7.0.0",
41 | "@babel/plugin-proposal-decorators": "^7.0.0",
42 | "@babel/plugin-proposal-do-expressions": "^7.0.0",
43 | "@babel/plugin-proposal-export-default-from": "^7.0.0",
44 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
45 | "@babel/plugin-proposal-function-sent": "^7.0.0",
46 | "@babel/plugin-proposal-json-strings": "^7.0.0",
47 | "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
48 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
49 | "@babel/plugin-proposal-numeric-separator": "^7.0.0",
50 | "@babel/plugin-proposal-optional-chaining": "^7.0.0",
51 | "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
52 | "@babel/plugin-proposal-throw-expressions": "^7.0.0",
53 | "@babel/plugin-syntax-dynamic-import": "^7.0.0",
54 | "@babel/plugin-syntax-import-meta": "^7.0.0",
55 | "@babel/plugin-transform-modules-commonjs": "^7.0.0",
56 | "@babel/preset-env": "^7.0.0",
57 | "@babel/preset-react": "^7.0.0",
58 | "@storybook/addon-actions": "^4.1.4",
59 | "@storybook/react": "^4.1.4",
60 | "babel-core": "^7.0.0-bridge.0",
61 | "babel-eslint": "^10.0.1",
62 | "babel-jest": "^23.6.0",
63 | "babel-loader": "^8.0.5",
64 | "babel-plugin-styled-components": "^1.10.0",
65 | "babel-plugin-transform-react-remove-prop-types": "^0.4.21",
66 | "copyfiles": "^2.0.0",
67 | "cross-env": "^5.0.0",
68 | "enzyme": "^3.8.0",
69 | "enzyme-adapter-react-16": "^1.7.1",
70 | "eslint": "^5.12.0",
71 | "eslint-config-airbnb": "^17.1.0",
72 | "eslint-import-resolver-webpack": "^0.10.1",
73 | "eslint-plugin-import": "^2.2.0",
74 | "eslint-plugin-jsx-a11y": "^6.0.2",
75 | "eslint-plugin-react": "^7.0.1",
76 | "file-loader": "^3.0.1",
77 | "happypack": "^5.0.1",
78 | "html-webpack-plugin": "^3.2.0",
79 | "jest-cli": "^23.6.0",
80 | "opn-cli": "^4.0.0",
81 | "raw-loader": "^1.0.0",
82 | "rimraf": "^2.6.1",
83 | "uglifyjs-webpack-plugin": "^2.1.1",
84 | "url-loader": "^1.1.2",
85 | "webpack": "^4.28.3",
86 | "webpack-cli": "^3.2.0",
87 | "webpack-dev-server": "^3.1.14",
88 | "webpack-md5-hash": "^0.0.6"
89 | },
90 | "dependencies": {
91 | "lodash": "^4.17.13",
92 | "prop-types": "^15.6.2",
93 | "react": "^16.7.0",
94 | "react-dom": "^16.7.0",
95 | "react-hot-loader": "^4.6.3",
96 | "react-modal": "^3.1.11",
97 | "react-router-dom": "^4.3.1",
98 | "styled-components": "^3.5.0-0",
99 | "styled-theme": "^0.3.3",
100 | "styled-tools": "^1.6.0"
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/private/jest/componentsMock.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Testing-components
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 |
5 | module.exports = new Proxy({}, {
6 | get: (target, property) => {
7 | const Mock = ({ children }) => {children}
8 |
9 | Mock.displayName = property
10 | Mock.propTypes = {
11 | children: PropTypes.any,
12 | }
13 |
14 | return Mock
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/private/jest/fileMock.js:
--------------------------------------------------------------------------------
1 | export default 'file'
2 |
--------------------------------------------------------------------------------
/private/jest/setupTests.js:
--------------------------------------------------------------------------------
1 | import { configure } from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | configure({ adapter: new Adapter() })
5 |
--------------------------------------------------------------------------------
/private/storybook/config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Storybook
2 | import React from 'react'
3 | import { configure, addDecorator } from '@storybook/react'
4 | import { BrowserRouter } from 'react-router-dom'
5 | import { ThemeProvider } from 'styled-components'
6 | import theme from 'components/themes/default'
7 |
8 | const req = require.context('components', true, /.stories.js$/)
9 |
10 | function loadStories() {
11 | req.keys().forEach(filename => req(filename))
12 | }
13 |
14 | addDecorator(story => (
15 |
16 | {story()}
17 |
18 | ))
19 |
20 | configure(loadStories, module)
21 |
--------------------------------------------------------------------------------
/private/storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../webpack.config')
2 |
3 | module.exports = storybookBaseConfig => Object.assign({}, storybookBaseConfig, {
4 | resolve: Object.assign({}, storybookBaseConfig.resolve, {
5 | modules: baseConfig.resolve.modules,
6 | }),
7 | module: Object.assign({}, storybookBaseConfig.module, {
8 | rules: storybookBaseConfig.module.rules.concat(baseConfig.module.rules.slice(1)),
9 | }),
10 | })
11 |
--------------------------------------------------------------------------------
/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegohaz/arc/c90e60c21bca877965e93142f23bf37c8456a122/public/icon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ARc - Atomic React
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/public/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegohaz/arc/c90e60c21bca877965e93142f23bf37c8456a122/public/thumbnail.png
--------------------------------------------------------------------------------
/src-example/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route } from 'react-router-dom'
3 | import { injectGlobal, ThemeProvider } from 'styled-components'
4 |
5 | import { HomePage, SamplePage, NotFoundPage } from 'components'
6 |
7 | // https://github.com/diegohaz/arc/wiki/Styling
8 | import theme from './themes/default'
9 |
10 | injectGlobal`
11 | body {
12 | margin: 0;
13 | }
14 | `
15 |
16 | const App = () => {
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default App
29 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Atom/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 |
5 | const Atom = styled.span`
6 | font-family: ${font('primary')};
7 | color: ${palette({ grayscale: 0 }, 1)};
8 | `
9 |
10 | Atom.propTypes = {
11 | palette: PropTypes.string,
12 | reverse: PropTypes.bool,
13 | }
14 |
15 | Atom.defaultProps = {
16 | palette: 'grayscale',
17 | }
18 |
19 | export default Atom
20 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Atom/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Atom from '.'
4 |
5 | storiesOf('Atom', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Atom/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Atom from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Badge/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 |
5 | const Badge = styled.span`
6 | font-family: ${font('primary')};
7 | font-size: 0.75rem;
8 | line-height: 1.5em;
9 | padding: 0.1em 0.3em;
10 | color: ${palette('grayscale', 0, true)};
11 | background-color: ${palette(1)};
12 | border-radius: 0.16667em;
13 | `
14 |
15 | Badge.propTypes = {
16 | palette: PropTypes.string,
17 | reverse: PropTypes.bool,
18 | }
19 |
20 | Badge.defaultProps = {
21 | palette: 'primary',
22 | }
23 |
24 | export default Badge
25 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Badge/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Badge from '.'
4 |
5 | storiesOf('Badge', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 | .add('another palette', () => (
13 | Hello
14 | ))
15 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Badge/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Badge from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Block/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 | import { ifProp } from 'styled-tools'
5 |
6 | const Block = styled.div`
7 | font-family: ${font('primary')};
8 | background-color: ${ifProp('opaque', palette(0, true), 'transparent')};
9 | color: ${palette({ grayscale: 0 }, 1)};
10 | `
11 |
12 | Block.propTypes = {
13 | palette: PropTypes.string,
14 | reverse: PropTypes.bool,
15 | opaque: PropTypes.bool,
16 | }
17 |
18 | Block.defaultProps = {
19 | palette: 'grayscale',
20 | }
21 |
22 | export default Block
23 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Block/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Block from '.'
4 |
5 | storiesOf('Block', module)
6 | .add('default', () => (
7 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
8 | ))
9 | .add('reverse', () => (
10 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
11 | ))
12 | .add('palette', () => (
13 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
14 | ))
15 | .add('palette reverse', () => (
16 |
17 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
18 |
19 | ))
20 | .add('palette opaque', () => (
21 |
22 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
23 |
24 | ))
25 | .add('palette opaque reverse', () => (
26 |
27 | Officia aliqua reprehenderit fugiat occaecat quis non eiusmod.
28 |
29 | ))
30 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Block/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Block from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import Link from 'react-router-dom/Link'
5 | import { font, palette } from 'styled-theme'
6 | import { ifProp } from 'styled-tools'
7 |
8 | const fontSize = ({ height }) => `${height / 40}rem`
9 |
10 | const backgroundColor = ({ transparent, disabled }) => transparent ? 'transparent' : palette(disabled ? 2 : 1)
11 |
12 | const foregroundColor = ({ transparent, disabled }) => transparent ? palette(disabled ? 2 : 1) : palette('grayscale', 0, true)
13 |
14 | const hoverBackgroundColor = ({ disabled, transparent }) => !disabled && !transparent && palette(0)
15 | const hoverForegroundColor = ({ disabled, transparent }) => !disabled && transparent && palette(0)
16 |
17 | const styles = css`
18 | display: inline-flex;
19 | font-family: ${font('primary')};
20 | align-items: center;
21 | white-space: nowrap;
22 | font-size: ${fontSize};
23 | border: 0.0625em solid ${ifProp('transparent', 'currentcolor', 'transparent')};
24 | height: 2.5em;
25 | justify-content: center;
26 | text-decoration: none;
27 | cursor: ${ifProp('disabled', 'default', 'pointer')};
28 | appearance: none;
29 | padding: 0 1em;
30 | border-radius: 0.125em;
31 | box-sizing: border-box;
32 | pointer-events: ${ifProp('disabled', 'none', 'auto')};
33 | transition: background-color 250ms ease-out, color 250ms ease-out, border-color 250ms ease-out;
34 | background-color: ${backgroundColor};
35 | color: ${foregroundColor};
36 |
37 | &:hover, &:focus, &:active {
38 | background-color: ${hoverBackgroundColor};
39 | color: ${hoverForegroundColor};
40 | }
41 |
42 | &:focus {
43 | outline: none
44 | }
45 | `
46 |
47 | const StyledLink = styled(({
48 | disabled, transparent, reverse, palette, height, theme, ...props
49 | }) => )`${styles}`
50 |
51 | const Anchor = styled.a`${styles}`
52 | const StyledButton = styled.button`${styles}`
53 |
54 | const Button = ({ type, ...props }) => {
55 | const { to, href } = props
56 | if (to) {
57 | return
58 | } if (href) {
59 | return
60 | }
61 | return
62 | }
63 |
64 | Button.propTypes = {
65 | disabled: PropTypes.bool,
66 | palette: PropTypes.string,
67 | transparent: PropTypes.bool,
68 | reverse: PropTypes.bool,
69 | height: PropTypes.number,
70 | type: PropTypes.string,
71 | to: PropTypes.string,
72 | href: PropTypes.string,
73 | }
74 |
75 | Button.defaultProps = {
76 | palette: 'primary',
77 | type: 'button',
78 | height: 40,
79 | }
80 |
81 | export default Button
82 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Button/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Button from '.'
4 |
5 | storiesOf('Button', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 | .add('another palette', () => (
13 | Hello
14 | ))
15 | .add('disabled', () => (
16 | Hello
17 | ))
18 | .add('transparent', () => (
19 | Hello
20 | ))
21 | .add('height', () => (
22 | Hello
23 | ))
24 | .add('link', () => (
25 | ARc repository
26 | ))
27 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Button/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Button from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders with different combination of props', () => {
8 | wrap({ disabled: true })
9 | wrap({ transparent: true })
10 | wrap({ disabled: true, transparent: true })
11 | })
12 |
13 | it('renders children when passed in', () => {
14 | const wrapper = wrap({ children: 'test' })
15 | expect(wrapper.contains('test')).toBe(true)
16 | })
17 |
18 | it('renders props when passed in', () => {
19 | const wrapper = wrap({ type: 'submit' })
20 | expect(wrapper.find({ type: 'submit' })).toHaveLength(1)
21 | })
22 |
23 | it('renders button by default', () => {
24 | const wrapper = wrap()
25 | expect(wrapper.find('button')).toHaveLength(1)
26 | })
27 |
28 | it('renders anchor when href is passed in', () => {
29 | const wrapper = wrap({ href: 'test' })
30 | expect(wrapper.find('a')).toHaveLength(1)
31 | })
32 |
33 | it('renders Link when to is passed in', () => {
34 | const wrapper = wrap({ to: 'test' }).dive()
35 | expect(wrapper.find('Link')).toHaveLength(1)
36 | })
37 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Caption/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 |
5 | const Caption = styled.caption`
6 | font-family: ${font('primary')};
7 | color: ${palette('grayscale', 1)};
8 | font-weight: 500;
9 | line-height: 2rem;
10 | font-size: 0.875rem;
11 | text-transform: uppercase;
12 | `
13 |
14 | Caption.propTypes = {
15 | reverse: PropTypes.bool,
16 | }
17 |
18 | export default Caption
19 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Caption/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Caption from '.'
4 |
5 | storiesOf('Caption', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Caption/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Caption from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Heading/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | const fontSize = ({ level }) => `${0.75 + (1 * (1 / level))}rem`
7 |
8 | const styles = css`
9 | font-family: ${font('primary')};
10 | font-weight: 500;
11 | font-size: ${fontSize};
12 | margin: 0;
13 | margin-top: 0.85714em;
14 | margin-bottom: 0.57142em;
15 | color: ${palette({ grayscale: 0 }, 1)};
16 | `
17 |
18 | const Heading = styled(({
19 | level, children, reverse, palette, theme, ...props
20 | }) => React.createElement(`h${level}`, props, children))`${styles}`
21 |
22 | Heading.propTypes = {
23 | level: PropTypes.number,
24 | children: PropTypes.node,
25 | palette: PropTypes.string,
26 | reverse: PropTypes.bool,
27 | }
28 |
29 | Heading.defaultProps = {
30 | level: 1,
31 | palette: 'grayscale',
32 | }
33 |
34 | export default Heading
35 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Heading/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Heading from '.'
4 |
5 | storiesOf('Heading', module)
6 | .add('default', () => (
7 | Id tempor duis non esse commodo fugiat excepteur nostrud.
8 | ))
9 | .add('palette', () => (
10 | Id tempor duis non esse commodo fugiat excepteur nostrud.
11 | ))
12 | .add('palette reverse', () => (
13 |
14 | Id tempor duis non esse commodo fugiat excepteur nostrud.
15 |
16 | ))
17 | .add('level 2', () => (
18 | Id tempor duis non esse commodo fugiat excepteur nostrud.
19 | ))
20 | .add('level 3', () => (
21 | Id tempor duis non esse commodo fugiat excepteur nostrud.
22 | ))
23 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Heading/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Heading from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders h1 by default', () => {
18 | const wrapper = wrap()
19 | expect(wrapper.find('h1')).toHaveLength(1)
20 | })
21 |
22 | it('renders hLevel when level is passed in', () => {
23 | const wrapper = wrap({ level: 2 })
24 | expect(wrapper.find('h2')).toHaveLength(1)
25 | })
26 |
--------------------------------------------------------------------------------
/src-example/components/atoms/HorizontalRule/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { palette } from 'styled-theme'
4 |
5 | const HorizontalRule = styled.hr`
6 | border: 1px solid ${palette(1, true)};
7 | border-width: 0 0 1px;
8 | width: 100%;
9 | `
10 |
11 | HorizontalRule.propTypes = {
12 | palette: PropTypes.string,
13 | reverse: PropTypes.bool,
14 | }
15 |
16 | HorizontalRule.defaultProps = {
17 | palette: 'grayscale',
18 | }
19 |
20 | export default HorizontalRule
21 |
--------------------------------------------------------------------------------
/src-example/components/atoms/HorizontalRule/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import HorizontalRule from '.'
4 |
5 | storiesOf('HorizontalRule', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('palette', () => (
10 |
11 | ))
12 | .add('palette reverse', () => (
13 |
14 | ))
15 |
--------------------------------------------------------------------------------
/src-example/components/atoms/HorizontalRule/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import HorizontalRule from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/arc.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/atomic-design.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/copy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/dist.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/docs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
8 |
9 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/jest.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/react-router.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/react.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/redux.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/icons/webpack.svg:
--------------------------------------------------------------------------------
1 |
2 | icon-square-big
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Example-components#icon
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 | import styled from 'styled-components'
5 | import { palette } from 'styled-theme'
6 | import { ifProp } from 'styled-tools'
7 |
8 | const fontSize = ({ width, height }) => {
9 | const size = width || height
10 | return size ? `${size / 16}rem` : '1.25em'
11 | }
12 |
13 | const Wrapper = styled.span`
14 | display: inline-block;
15 | font-size: ${fontSize};
16 | color: ${ifProp('palette', palette({ grayscale: 0 }, 1), 'currentcolor')};
17 | width: 1em;
18 | height: 1em;
19 | margin: 0.1em;
20 | box-sizing: border-box;
21 |
22 | & > svg {
23 | width: 100%;
24 | height: 100%;
25 | fill: currentcolor;
26 | stroke: currentcolor;
27 | }
28 | `
29 |
30 | const Icon = ({ icon, ...props }) => {
31 | const svg = require(`!raw-loader!./icons/${icon}.svg`)
32 | return
33 | }
34 |
35 | Icon.propTypes = {
36 | icon: PropTypes.string.isRequired,
37 | width: PropTypes.number,
38 | height: PropTypes.number,
39 | palette: PropTypes.string,
40 | reverse: PropTypes.bool,
41 | }
42 |
43 | export default Icon
44 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/index.stories.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Example-components#icon
2 | import React from 'react'
3 | import { storiesOf } from '@storybook/react'
4 | import Icon from '.'
5 |
6 | storiesOf('Icon', module)
7 | .add('default', () => (
8 |
9 | ))
10 | .add('palette', () => (
11 |
12 | ))
13 | .add('palette reverse', () => (
14 |
15 | ))
16 | .add('height', () => (
17 |
18 | ))
19 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Icon/index.test.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Example-components#icon
2 | import React from 'react'
3 | import { shallow } from 'enzyme'
4 | import Icon from '.'
5 |
6 | const wrap = (props = {}) => shallow( ).dive()
7 |
8 | it('renders with different combination of props', () => {
9 | wrap({ height: 40 })
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Input/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 | import { ifProp } from 'styled-tools'
6 |
7 | const fontSize = ({ height }) => `${height / 35.5555555556}rem`
8 |
9 | const styles = css`
10 | font-family: ${font('primary')};
11 | display: block;
12 | width: 100%;
13 | margin: 0;
14 | box-sizing: border-box;
15 | font-size: ${fontSize};
16 | padding: ${ifProp({ type: 'textarea' }, '0.4444444444em', '0 0.4444444444em')};
17 | height: ${ifProp({ type: 'textarea' }, 'auto', '2.2222222222em')};
18 | color: ${palette('grayscale', 0)};
19 | background-color: ${palette('grayscale', 0, true)};
20 | border: 1px solid ${ifProp('invalid', palette('danger', 2), palette('grayscale', 3))};
21 | border-radius: 2px;
22 |
23 | &[type=checkbox], &[type=radio] {
24 | display: inline-block;
25 | border: 0;
26 | border-radius: 0;
27 | width: auto;
28 | height: auto;
29 | margin: 0 0.2rem 0 0;
30 | }
31 | `
32 |
33 | const StyledTextarea = styled.textarea`${styles}`
34 | const StyledSelect = styled.select`${styles}`
35 | const StyledInput = styled.input`${styles}`
36 |
37 | const Input = ({ ...props }) => {
38 | const { type } = props
39 | if (type === 'textarea') {
40 | return
41 | } if (type === 'select') {
42 | return
43 | }
44 | return
45 | }
46 |
47 | Input.propTypes = {
48 | type: PropTypes.string,
49 | reverse: PropTypes.bool,
50 | height: PropTypes.number,
51 | invalid: PropTypes.bool,
52 | }
53 |
54 | Input.defaultProps = {
55 | type: 'text',
56 | height: 40,
57 | }
58 |
59 | export default Input
60 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Input/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Input from '.'
4 |
5 | storiesOf('Input', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('reverse', () => (
10 |
11 | ))
12 | .add('height', () => (
13 |
14 | ))
15 | .add('invalid', () => (
16 |
17 | ))
18 | .add('type textarea', () => (
19 |
20 | ))
21 | .add('type checkbox', () => (
22 |
23 | ))
24 | .add('type radio', () => (
25 |
26 | ))
27 | .add('type select', () => (
28 |
29 | Option 1
30 | Option 2
31 | Option 3
32 |
33 | ))
34 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Input/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Input from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ type: 'text' })
9 | expect(wrapper.find({ type: 'text' })).toHaveLength(1)
10 | })
11 |
12 | it('renders input by default', () => {
13 | const wrapper = wrap()
14 | expect(wrapper.find('input')).toHaveLength(1)
15 | })
16 |
17 | it('renders select when type is select', () => {
18 | const wrapper = wrap({ type: 'select' })
19 | expect(wrapper.find('select')).toHaveLength(1)
20 | })
21 |
22 | it('renders textarea when type is textarea', () => {
23 | const wrapper = wrap({ type: 'textarea' })
24 | expect(wrapper.find('textarea')).toHaveLength(1)
25 | })
26 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Label/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 |
5 | const Label = styled.label`
6 | font-family: ${font('primary')};
7 | color: ${palette('grayscale', 1)};
8 | font-size: 1rem;
9 | line-height: 2em;
10 | `
11 |
12 | Label.propTypes = {
13 | reverse: PropTypes.bool,
14 | }
15 |
16 | export default Label
17 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Label/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Label from '.'
4 |
5 | storiesOf('Label', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Label/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Label from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ htmlFor: 'foo' })
14 | expect(wrapper.find({ htmlFor: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Link/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 | import NavLink from 'react-router-dom/NavLink'
6 |
7 | const styles = css`
8 | font-family: ${font('primary')};
9 | text-decoration: none;
10 | font-weight: 500;
11 | color: ${palette({ grayscale: 0 }, 1)};
12 |
13 | &:hover {
14 | text-decoration: underline;
15 | }
16 | `
17 |
18 | const StyledNavLink = styled(({
19 | theme, reverse, palette, ...props
20 | }) => )`${styles}`
21 |
22 | const Anchor = styled.a`${styles}`
23 |
24 | const Link = ({ ...props }) => {
25 | const { to } = props
26 | if (to) {
27 | return
28 | }
29 | return
30 | }
31 |
32 | Link.propTypes = {
33 | palette: PropTypes.string,
34 | reverse: PropTypes.bool,
35 | to: PropTypes.string,
36 | }
37 |
38 | Link.defaultProps = {
39 | palette: 'primary',
40 | }
41 |
42 | export default Link
43 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Link/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Link from '.'
4 |
5 | storiesOf('Link', module)
6 | .add('default', () => (
7 | ARc repository
8 | ))
9 | .add('reverse', () => (
10 | ARc repository
11 | ))
12 | .add('another palette', () => (
13 | ARc repository
14 | ))
15 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Link/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Link from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders anchor by default', () => {
18 | const wrapper = wrap()
19 | expect(wrapper.find('a')).toHaveLength(1)
20 | })
21 |
22 | it('renders Link when prop to is passed in', () => {
23 | const wrapper = wrap({ to: 'test' }).dive()
24 | expect(wrapper.find('NavLink')).toHaveLength(1)
25 | })
26 |
--------------------------------------------------------------------------------
/src-example/components/atoms/List/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | const styles = css`
7 | font-family: ${font('primary')};
8 | margin: 1rem 0;
9 | padding-left: 1.6rem;
10 | line-height: 1.7rem;
11 | color: ${palette({ grayscale: 0 }, 1)};
12 | `
13 |
14 | const Ol = styled.ol`${styles}`
15 | const Ul = styled.ul`${styles}`
16 |
17 | const List = ({ ordered, children, ...props }) => {
18 | return React.createElement(ordered ? Ol : Ul, props, children)
19 | }
20 |
21 | List.propTypes = {
22 | ordered: PropTypes.bool,
23 | palette: PropTypes.string,
24 | reverse: PropTypes.bool,
25 | children: PropTypes.any,
26 | }
27 |
28 | List.defaultProps = {
29 | palette: 'grayscale',
30 | }
31 |
32 | export default List
33 |
--------------------------------------------------------------------------------
/src-example/components/atoms/List/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import List from '.'
4 |
5 | storiesOf('List', module)
6 | .add('default', () => (
7 |
8 | Item 1
9 | Item 2
10 | Item 3
11 |
12 | ))
13 | .add('ordered', () => (
14 |
15 | Item 1
16 | Item 2
17 | Item 3
18 |
19 | ))
20 | .add('palette', () => (
21 |
22 | Item 1
23 | Item 2
24 | Item 3
25 |
26 | ))
27 | .add('palette reverse', () => (
28 |
29 | Item 1
30 | Item 2
31 | Item 3
32 |
33 | ))
34 |
--------------------------------------------------------------------------------
/src-example/components/atoms/List/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import List from '.'
4 |
5 | const wrap = (props = {}) => shallow(
).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders ul by default', () => {
18 | const wrapper = wrap()
19 | expect(wrapper.find('ul')).toHaveLength(1)
20 | })
21 |
22 | it('renders ol when ordered prop is passed in', () => {
23 | const wrapper = wrap({ ordered: true })
24 | expect(wrapper.find('ol')).toHaveLength(1)
25 | })
26 |
--------------------------------------------------------------------------------
/src-example/components/atoms/LogoImage/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import logo from './logo.svg'
4 |
5 | const LogoImage = props =>
6 |
7 | export default LogoImage
8 |
--------------------------------------------------------------------------------
/src-example/components/atoms/LogoImage/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import LogoImage from '.'
4 |
5 | storiesOf('LogoImage', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/atoms/LogoImage/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import LogoImage from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/atoms/LogoImage/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Paragraph/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 |
5 | const Paragraph = styled.p`
6 | font-family: ${font('primary')};
7 | color: ${palette('grayscale', 0)};
8 | font-size: 1rem;
9 | line-height: 1.3;
10 | margin: 1rem 0 0;
11 | `
12 |
13 | Paragraph.propTypes = {
14 | reverse: PropTypes.bool,
15 | }
16 |
17 | export default Paragraph
18 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Paragraph/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Paragraph from '.'
4 |
5 | storiesOf('Paragraph', module)
6 | .add('default', () => (
7 |
8 | Nisi eu eiusmod cupidatat aute laboris commodo excepteur esse dolore incididunt incididunt aliquip pariatur est minim officia sit. Nulla pariatur duis duis quis commodo cupidatat voluptate enim culpa elit adipisicing do cupidatat sint anim. Cillum elit magna occaecat proident sit cupidatat ad quis sunt id culpa culpa. Ad duis nulla in incididunt amet consequat officia ad voluptate voluptate. Pariatur eiusmod ullamco cupidatat non magna officia aute magna deserunt qui aute dolor eu. Qui amet non ex cillum sunt ad velit consequat ipsum velit.
9 |
10 | ))
11 | .add('reverse', () => (
12 |
13 | Nisi eu eiusmod cupidatat aute laboris commodo excepteur esse dolore incididunt incididunt aliquip pariatur est minim officia sit. Nulla pariatur duis duis quis commodo cupidatat voluptate enim culpa elit adipisicing do cupidatat sint anim. Cillum elit magna occaecat proident sit cupidatat ad quis sunt id culpa culpa. Ad duis nulla in incididunt amet consequat officia ad voluptate voluptate. Pariatur eiusmod ullamco cupidatat non magna officia aute magna deserunt qui aute dolor eu. Qui amet non ex cillum sunt ad velit consequat ipsum velit.
14 |
15 | ))
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Paragraph/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Paragraph from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/PreformattedText/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { font, palette } from 'styled-theme'
4 | import { ifProp } from 'styled-tools'
5 |
6 | const PreformattedText = styled.pre`
7 | display: ${ifProp('block', 'block', 'inline')};
8 | font-family: ${font('pre')};
9 | color: ${palette({ grayscale: 0 }, 1)};
10 | background-color: ${palette('grayscale', 1, true)};
11 | padding: ${ifProp('block', '1em', '0 0.5em')};
12 | white-space: ${ifProp('wrapped', 'pre-wrap', 'nowrap')};
13 | overflow: auto;
14 | line-height: 150%;
15 | `
16 |
17 | PreformattedText.propTypes = {
18 | palette: PropTypes.string,
19 | reverse: PropTypes.bool,
20 | block: PropTypes.bool,
21 | wrapped: PropTypes.bool,
22 | }
23 |
24 | PreformattedText.defaultProps = {
25 | palette: 'grayscale',
26 | }
27 |
28 | export default PreformattedText
29 |
--------------------------------------------------------------------------------
/src-example/components/atoms/PreformattedText/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import PreformattedText from '.'
4 |
5 | storiesOf('PreformattedText', module)
6 | .add('default', () => (
7 | git clone https://github.com/diegohaz/arc
8 | ))
9 | .add('reverse', () => (
10 | git clone https://github.com/diegohaz/arc
11 | ))
12 | .add('block', () => (
13 | git clone https://github.com/diegohaz/arc
14 | ))
15 | .add('block reverse', () => (
16 | git clone https://github.com/diegohaz/arc
17 | ))
18 |
--------------------------------------------------------------------------------
/src-example/components/atoms/PreformattedText/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import PreformattedText from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Spinner/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled, { keyframes } from 'styled-components'
3 | import { palette } from 'styled-theme'
4 |
5 | const spin = keyframes`
6 | 0% { transform: rotate(0deg); }
7 | 100% { transform: rotate(360deg); }
8 | `
9 |
10 | const Spinner = styled.div`
11 | position: relative;
12 | border: 0.2em solid ${palette('grayscale', 1, true)};
13 | border-bottom-color: ${palette(1)};
14 | border-radius: 50%;
15 | margin: 0 auto;
16 | width: 1em;
17 | height: 1em;
18 | animation: ${spin} 1s linear infinite;
19 | `
20 |
21 | Spinner.propTypes = {
22 | palette: PropTypes.string,
23 | reverse: PropTypes.bool,
24 | }
25 |
26 | Spinner.defaultProps = {
27 | palette: 'primary',
28 | }
29 |
30 | export default Spinner
31 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Spinner/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Spinner from '.'
4 |
5 | storiesOf('Spinner', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('reverse', () => (
10 |
11 | ))
12 | .add('another palette', () => (
13 |
14 | ))
15 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Spinner/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Spinner from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableCell/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 |
5 | const styles = css`
6 | text-align: left;
7 | padding: 0.75em;
8 | `
9 |
10 | const Th = styled.th`${styles}`
11 | const Td = styled.td`${styles}`
12 |
13 | const TableCell = ({ heading, children, ...props }) => {
14 | return React.createElement(heading ? Th : Td, props, children)
15 | }
16 |
17 | TableCell.propTypes = {
18 | heading: PropTypes.bool,
19 | children: PropTypes.any,
20 | }
21 |
22 | export default TableCell
23 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableCell/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import TableCell from '.'
4 |
5 | storiesOf('TableCell', module)
6 | .add('default', () => (
7 |
8 |
9 |
10 | Heading Cell 1
11 | Heading Cell 2
12 | Heading Cell 3
13 |
14 |
15 |
16 |
17 | Cell 1
18 | Cell 2
19 | Cell 3
20 |
21 |
22 |
23 | ))
24 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableCell/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import TableCell from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders td by default', () => {
18 | const wrapper = wrap()
19 | expect(wrapper.find('td')).toHaveLength(1)
20 | })
21 |
22 | it('renders th when prop heading is passed in', () => {
23 | const wrapper = wrap({ heading: true })
24 | expect(wrapper.find('th')).toHaveLength(1)
25 | })
26 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableRow/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { palette } from 'styled-theme'
4 |
5 | const backgroundColor = ({ filled }) => palette('grayscale', filled ? 1 : 0, true)
6 |
7 | const TableRow = styled.tr`
8 | background-color: ${backgroundColor};
9 | `
10 |
11 | TableRow.propTypes = {
12 | filled: PropTypes.bool,
13 | reverse: PropTypes.bool,
14 | }
15 |
16 | export default TableRow
17 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableRow/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import TableRow from '.'
4 |
5 | storiesOf('TableRow', module)
6 | .add('default', () => (
7 |
8 |
9 |
10 | Heading Cell 1
11 | Heading Cell 2
12 | Heading Cell 3
13 |
14 |
15 |
16 |
17 | Cell 1
18 | Cell 2
19 | Cell 3
20 |
21 |
22 | Cell 1
23 | Cell 2
24 | Cell 3
25 |
26 |
27 |
28 | ))
29 |
--------------------------------------------------------------------------------
/src-example/components/atoms/TableRow/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import TableRow from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders with different combination of props', () => {
8 | wrap({ filled: true })
9 | })
10 |
11 | it('renders children when passed in', () => {
12 | const wrapper = wrap({ children: 'test' })
13 | expect(wrapper.contains('test')).toBe(true)
14 | })
15 |
16 | it('renders props when passed in', () => {
17 | const wrapper = wrap({ id: 'foo' })
18 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
19 | })
20 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { font } from 'styled-theme'
5 | import { ifProp } from 'styled-tools'
6 |
7 | const opposites = {
8 | top: 'bottom',
9 | right: 'left',
10 | bottom: 'top',
11 | left: 'right',
12 | }
13 |
14 | export const opposite = ({ position }) => opposites[position]
15 |
16 | export const perpendicular = ({ position }) => position === 'left' || position === 'right' ? 'top' : 'left'
17 |
18 | export const perpendicularOpposite = props => opposites[perpendicular(props)]
19 |
20 | export const perpendicularAxis = ({ position }) => position === 'left' || position === 'right' ? 'Y' : 'X'
21 |
22 | const backgroundColor = ifProp('reverse', 'rgba(255, 255, 255, 0.85)', 'rgba(0, 0, 0, 0.85)')
23 |
24 | const styles = css`
25 | position: relative;
26 |
27 | &:before,
28 | &:after {
29 | position: absolute;
30 | pointer-events: none;
31 | display: block;
32 | opacity: 0;
33 | transition: opacity 100ms ease-in-out, ${opposite} 100ms ease-in-out;
34 | will-change: ${opposite};
35 | }
36 |
37 | &:hover:before,
38 | &:focus:before {
39 | opacity: 1;
40 | ${opposite}: calc(100% + 1rem);
41 | }
42 |
43 | &:hover:after,
44 | &:focus:after {
45 | opacity: 1;
46 | ${opposite}: 100%;
47 | }
48 |
49 | &:before {
50 | content: attr(data-title);
51 | font-family: ${font('primary')};
52 | white-space: nowrap;
53 | text-transform: none;
54 | font-size: 0.8125rem;
55 | line-height: 1.5;
56 | text-align: center;
57 | color: ${ifProp('reverse', 'black', 'white')};
58 | background-color: ${backgroundColor};
59 | border-radius: 0.15384em;
60 | padding: 0.75em 1em;
61 | ${opposite}: calc(100% + 2rem);
62 | ${({ align }) => {
63 | switch (align) {
64 | case 'start': return css`${perpendicular}: 0;`
65 | case 'center': return css`
66 | ${perpendicular}: 50%;
67 | transform: translate${perpendicularAxis}(-50%);
68 | `
69 | default: return css`
70 | ${perpendicularOpposite}: 0;
71 | `
72 | }
73 | }}
74 | }
75 |
76 | &:after {
77 | ${opposite}: calc(100% + 1rem);
78 | ${perpendicular}: 50%;
79 | border: solid transparent;
80 | content: '';
81 | height: 0;
82 | width: 0;
83 | border-${({ position }) => position}-color: ${backgroundColor};
84 | border-width: 0.5rem;
85 | margin-${perpendicular}: -0.5rem;
86 | }
87 | `
88 |
89 | const Tooltip = styled(({
90 | position, align, reverse, children, theme, ...props
91 | }) => React.cloneElement(children, props))`${styles}`
92 |
93 | Tooltip.propTypes = {
94 | position: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
95 | align: PropTypes.oneOf(['start', 'center', 'end']),
96 | reverse: PropTypes.bool,
97 | 'data-title': PropTypes.string.isRequired,
98 | children: PropTypes.element.isRequired,
99 | }
100 |
101 | Tooltip.defaultProps = {
102 | position: 'top',
103 | align: 'center',
104 | tabIndex: 0,
105 | }
106 |
107 | export default Tooltip
108 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Tooltip/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Tooltip from '.'
4 |
5 | storiesOf('Tooltip', module)
6 | .add('default', () => (
7 |
12 | ))
13 | .add('reverse', () => (
14 |
19 | ))
20 | .add('position right', () => (
21 |
26 | ))
27 | .add('position bottom', () => (
28 |
33 | ))
34 | .add('position left', () => (
35 |
40 | ))
41 | .add('align start', () => (
42 |
47 | ))
48 | .add('align end', () => (
49 |
54 | ))
55 |
--------------------------------------------------------------------------------
/src-example/components/atoms/Tooltip/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Tooltip, { opposite, perpendicular, perpendicularAxis } from '.'
4 |
5 | const wrap = (props = {}) => (
6 | shallow(test ).dive()
7 | )
8 |
9 | it('renders with different props', () => {
10 | wrap({ align: 'start' })
11 | wrap({ align: 'end' })
12 | })
13 |
14 | it('renders children when passed in', () => {
15 | const wrapper = wrap()
16 | expect(wrapper.contains('test')).toBe(true)
17 | })
18 |
19 | it('renders props when passed in', () => {
20 | const wrapper = wrap({ id: 'foo' })
21 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
22 | })
23 |
24 | it('renders data-title', () => {
25 | const wrapper = wrap()
26 | expect(wrapper.find({ 'data-title': 'title' })).toHaveLength(1)
27 | })
28 |
29 | it('renders tabIndex', () => {
30 | const wrapper = wrap()
31 | expect(wrapper.find({ tabIndex: 0 })).toHaveLength(1)
32 | })
33 |
34 | test('opposite', () => {
35 | expect(opposite({ position: 'top' })).toBe('bottom')
36 | expect(opposite({ position: 'right' })).toBe('left')
37 | expect(opposite({ position: 'bottom' })).toBe('top')
38 | expect(opposite({ position: 'left' })).toBe('right')
39 | })
40 |
41 | test('perpendicular', () => {
42 | expect(perpendicular({ position: 'top' })).toBe('left')
43 | expect(perpendicular({ position: 'right' })).toBe('top')
44 | expect(perpendicular({ position: 'bottom' })).toBe('left')
45 | expect(perpendicular({ position: 'left' })).toBe('top')
46 | })
47 |
48 | test('perpendicularAxis', () => {
49 | expect(perpendicularAxis({ position: 'top' })).toBe('X')
50 | expect(perpendicularAxis({ position: 'right' })).toBe('Y')
51 | expect(perpendicularAxis({ position: 'bottom' })).toBe('X')
52 | expect(perpendicularAxis({ position: 'left' })).toBe('Y')
53 | })
54 |
--------------------------------------------------------------------------------
/src-example/components/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design#do-not-worry
2 | const req = require.context('.', true, /\.\/[^/]+\/[^/]+\/index\.js$/)
3 |
4 | req.keys().forEach((key) => {
5 | const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, '$1')
6 | module.exports[componentName] = req(key).default
7 | })
8 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Blockquote/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | const StyledBlockquote = styled.blockquote`
7 | position: relative;
8 | font-family: ${font('quote')};
9 | font-style: italic;
10 | font-size: 1.2rem;
11 | line-height: 2rem;
12 | box-sizing: border-box;
13 | color: ${palette('grayscale', 1)};
14 | border-left: 5px solid ${palette('grayscale', 2, true)};
15 | margin: 1rem 0;
16 | padding: 0.5rem 0 0.5rem 1.5rem;
17 | `
18 |
19 | const Cite = styled.cite`
20 | display: block;
21 | font-family: ${font('primary')};
22 | font-weight: 300;
23 | font-style: normal;
24 | margin-top: 0.4rem;
25 | `
26 |
27 | const Blockquote = ({ cite, children, ...props }) => {
28 | return (
29 |
30 | {children}
31 | {cite && {cite} }
32 |
33 | )
34 | }
35 |
36 | Blockquote.propTypes = {
37 | cite: PropTypes.string,
38 | children: PropTypes.node,
39 | reverse: PropTypes.bool,
40 | }
41 |
42 | export default Blockquote
43 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Blockquote/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Blockquote } from 'components'
4 |
5 | storiesOf('Blockquote', module)
6 | .add('default', () => (
7 |
8 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
9 |
10 | ))
11 | .add('reverse', () => (
12 |
13 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
14 |
15 | ))
16 | .add('with cite', () => (
17 |
18 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
19 |
20 | ))
21 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Blockquote/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Blockquote from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders cite when passed in', () => {
18 | const wrapper = wrap({ cite: 'foo' })
19 | expect(wrapper.contains('foo')).toBe(true)
20 | })
21 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Feature/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { ifProp } from 'styled-tools'
5 |
6 | import {
7 | Icon, Link, Paragraph, Heading, Badge, PreformattedText,
8 | } from 'components'
9 |
10 | const Wrapper = styled.div`
11 | position: relative;
12 | display: flex;
13 | padding: 1rem;
14 | box-sizing: border-box;
15 | opacity: ${ifProp('soon', 0.4, 1)};
16 | @media screen and (max-width: 640px) {
17 | padding: 0.5rem;
18 | }
19 | `
20 |
21 | const StyledIcon = styled(Icon)`
22 | flex: none;
23 | @media screen and (max-width: 640px) {
24 | width: 32px;
25 | }
26 | `
27 |
28 | const Text = styled.div`
29 | margin-left: 1rem;
30 | overflow: auto;
31 | > :first-child {
32 | margin: 0;
33 | }
34 | `
35 |
36 | const StyledBadge = styled(Badge)`
37 | position: absolute;
38 | top: 1rem;
39 | right: 1rem;
40 | @media screen and (max-width: 640px) {
41 | top: 0.5rem;
42 | right: 0.5rem;
43 | }
44 | `
45 |
46 | const Feature = ({
47 | icon, title, link, code, children, ...props
48 | }) => {
49 | const { soon } = props
50 | return (
51 |
52 | {icon && }
53 |
54 |
55 | {link ? {title} : title}
56 |
57 | {children}
58 | {code && {code} }
59 |
60 | {soon && soon }
61 |
62 | )
63 | }
64 |
65 | Feature.propTypes = {
66 | title: PropTypes.string.isRequired,
67 | icon: PropTypes.string,
68 | link: PropTypes.string,
69 | soon: PropTypes.bool,
70 | children: PropTypes.any,
71 | code: PropTypes.node,
72 | }
73 |
74 | export default Feature
75 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Feature/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Feature } from 'components'
4 |
5 | storiesOf('Feature', module)
6 | .add('default', () => (
7 |
8 | Ullamco duis in labore consectetur ad exercitation esse esse duis mollit sint.
9 |
10 | ))
11 | .add('with link', () => (
12 |
13 | Ullamco duis in labore consectetur ad exercitation esse esse duis mollit sint.
14 |
15 | ))
16 | .add('with icon', () => (
17 |
18 | Ullamco duis in labore consectetur ad exercitation esse esse duis mollit sint.
19 |
20 | ))
21 | .add('with code', () => (
22 |
23 | Ullamco duis in labore consectetur ad exercitation esse esse duis mollit sint.
24 |
25 | ))
26 | .add('with soon', () => (
27 |
28 | Ullamco duis in labore consectetur ad exercitation esse esse duis mollit sint.
29 |
30 | ))
31 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Feature/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Feature from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders title', () => {
18 | const wrapper = wrap()
19 | expect(wrapper.contains('Test')).toBe(true)
20 | })
21 |
22 | it('renders code', () => {
23 | const wrapper = wrap({ code: 'npm run build' })
24 | expect(wrapper.contains('npm run build')).toBe(true)
25 | })
26 |
27 | it('renders icon when passed in', () => {
28 | const wrapper = wrap({ icon: 'test' })
29 | expect(wrapper.find({ icon: 'test' })).toHaveLength(1)
30 | })
31 |
32 | it('renders link when passed in', () => {
33 | const wrapper = wrap({ link: 'test' })
34 | expect(wrapper.find({ href: 'test' })).toHaveLength(1)
35 | })
36 |
37 | it('renders badge when prop soon is passed in', () => {
38 | const wrapper = wrap({ soon: true })
39 | expect(wrapper.contains('soon')).toBe(true)
40 | })
41 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Field/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import { Label, Input, Block } from 'components'
6 |
7 | const Error = styled(Block)`
8 | margin: 0.5rem 0 0;
9 | `
10 |
11 | const Wrapper = styled.div`
12 | margin-bottom: 1rem;
13 | input[type="checkbox"],
14 | input[type="radio"] {
15 | margin-right: 0.5rem;
16 | }
17 | label {
18 | vertical-align: middle;
19 | }
20 | `
21 |
22 | const Field = ({
23 | error, name, invalid, label, type, ...props
24 | }) => {
25 | const inputProps = {
26 | id: name, name, type, invalid, 'aria-describedby': `${name}Error`, ...props,
27 | }
28 | const renderInputFirst = type === 'checkbox' || type === 'radio'
29 | return (
30 |
31 | {renderInputFirst && }
32 | {label && {label} }
33 | {renderInputFirst || }
34 | {invalid && error
35 | && (
36 |
37 | {error}
38 |
39 | )
40 | }
41 |
42 | )
43 | }
44 |
45 | Field.propTypes = {
46 | name: PropTypes.string.isRequired,
47 | invalid: PropTypes.bool,
48 | error: PropTypes.string,
49 | label: PropTypes.string,
50 | type: PropTypes.string,
51 | }
52 |
53 | Field.defaultProps = {
54 | type: 'text',
55 | }
56 |
57 | export default Field
58 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Field/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Field } from 'components'
4 |
5 | storiesOf('Field', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('with label', () => (
10 |
11 | ))
12 | .add('invalid', () => (
13 |
14 | ))
15 | .add('invalid with error message', () => (
16 |
17 | ))
18 | .add('type textarea', () => (
19 |
20 | ))
21 | .add('type select', () => (
22 |
23 | ))
24 | .add('type checkbox', () => (
25 |
26 | ))
27 | .add('type radio', () => (
28 |
29 | ))
30 | .add('type radio invalid with error message', () => (
31 |
32 | ))
33 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Field/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Field from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders input props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
12 | it('renders name', () => {
13 | const wrapper = wrap()
14 | expect(wrapper.find({ name: 'name' })).toHaveLength(1)
15 | })
16 |
17 | it('renders label when passed in', () => {
18 | const wrapper = wrap({ label: 'foo label' })
19 | expect(wrapper.contains('foo label')).toBe(true)
20 | })
21 |
22 | it('does not render error when passed in without invalid', () => {
23 | const wrapper = wrap({ error: 'foo error' })
24 | expect(wrapper.contains('foo error')).toBe(false)
25 | })
26 |
27 | it('renders error when passed in along with invalid', () => {
28 | const wrapper = wrap({ error: 'foo error', invalid: true })
29 | expect(wrapper.contains('foo error')).toBe(true)
30 | })
31 |
32 | it('renders label after input when type is checkbox', () => {
33 | const wrapper = wrap({ type: 'checkbox', label: 'foo label' })
34 | expect(wrapper.childAt(0).is('Input')).toBe(true)
35 | expect(wrapper.childAt(1).is('Label')).toBe(true)
36 | })
37 |
38 | it('renders label after input when type is radio', () => {
39 | const wrapper = wrap({ type: 'radio', label: 'foo label' })
40 | expect(wrapper.childAt(0).is('Input')).toBe(true)
41 | expect(wrapper.childAt(1).is('Label')).toBe(true)
42 | })
43 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconButton/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css, keyframes } from 'styled-components'
4 | import { ifProp, prop } from 'styled-tools'
5 |
6 | import { Icon, Button } from 'components'
7 |
8 | const fadeIn = keyframes`
9 | 0% { display: none; opacity: 0; }
10 | 1% { display: block; opacity: 0; }
11 | 100% { display: block; opacity: 1; }
12 | `
13 |
14 | const StyledButton = styled(Button)`
15 | max-width: ${props => props.hasText && !props.collapsed ? '100%' : '2.5em'};
16 | width: ${ifProp('hasText', 'auto', '2.5em')};
17 | padding: ${ifProp('hasText', '0 0.4375em', 0)};
18 | flex: 0 0 2.5em;
19 | box-sizing: border-box;
20 | ${ifProp('collapsed', css`
21 | overflow: hidden;
22 | transition: max-width 250ms ease-in-out;
23 | will-change: max-width;
24 | & .text {
25 | display: none;
26 | }
27 | &:hover {
28 | max-width: 100%;
29 | & .text {
30 | display: block;
31 | animation: ${fadeIn} 250ms;
32 | }
33 | }
34 | `)}
35 | ${ifProp('responsive', css`
36 | @media screen and (max-width: ${prop('breakpoint')}px) {
37 | width: auto;
38 | flex: 0 !important;
39 | }
40 | `)}
41 | `
42 |
43 | const Text = styled.span`
44 | padding: 0.4375em;
45 | @media screen and (max-width: ${prop('breakpoint')}px) {
46 | display: ${ifProp('responsive', 'none !important')};
47 | }
48 | `
49 |
50 | const Wrapper = styled.div`
51 | display: flex;
52 | align-items: center;
53 | justify-content: center;
54 | width: 100%;
55 | height: 100%;
56 | `
57 |
58 | const StyledIcon = styled(Icon)`
59 | flex: none;
60 | `
61 |
62 | const IconButton = ({ icon, children, ...props }) => {
63 | const {
64 | breakpoint, right, responsive, height,
65 | } = props
66 | const iconElement =
67 | return (
68 |
69 |
70 | {right || iconElement}
71 | {children
72 | && {children}
73 | }
74 | {right && iconElement}
75 |
76 |
77 | )
78 | }
79 |
80 | IconButton.propTypes = {
81 | icon: PropTypes.string.isRequired,
82 | responsive: PropTypes.bool,
83 | breakpoint: PropTypes.number,
84 | collapsed: PropTypes.bool,
85 | right: PropTypes.bool,
86 | height: PropTypes.number,
87 | children: PropTypes.node,
88 | }
89 |
90 | IconButton.defaultProps = {
91 | breakpoint: 420,
92 | }
93 |
94 | export default IconButton
95 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconButton/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { IconButton } from 'components'
4 |
5 | storiesOf('IconButton', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('transparent', () => (
10 | Hello
11 | ))
12 | .add('with icon on right', () => (
13 | Hello
14 | ))
15 | .add('responsive', () => (
16 | Decrease panel width
17 | ))
18 | .add('responsive with breakpoint', () => (
19 | Decrease panel width to 300
20 | ))
21 | .add('without text', () => (
22 |
23 | ))
24 | .add('collapsed', () => (
25 | Hello
26 | ))
27 | .add('height', () => (
28 | Hello
29 | ))
30 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconButton/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import IconButton from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('mounts with different combination of props', () => {
8 | mount(test )
9 | mount(test )
10 | mount(test )
11 | mount(test )
12 | mount(test )
13 | mount( )
14 | mount( )
15 | mount( )
16 | mount( )
17 | })
18 |
19 | it('renders children when passed in', () => {
20 | const wrapper = wrap({ children: 'test' })
21 | expect(wrapper.contains('test')).toBe(true)
22 | })
23 |
24 | it('renders props when passed in', () => {
25 | const wrapper = wrap({ id: 'foo' })
26 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
27 | })
28 |
29 | it('passes height to icon', () => {
30 | const wrapper = wrap({ height: 20 })
31 | expect(wrapper.find({ height: 20 / 2.5 })).toHaveLength(1)
32 | })
33 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconLink/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import { Icon, Link } from 'components'
6 |
7 | const fontSize = ({ height }) => height ? `${height / 3 / 16}rem` : '0.75em'
8 |
9 | const margin = ({ hasText, right }) => {
10 | if (hasText) {
11 | return right ? '0 0 0 0.25em' : '0 0.25em 0 0'
12 | }
13 | return 0
14 | }
15 |
16 | const StyledIcon = styled(Icon)`
17 | font-size: ${fontSize};
18 | margin: ${margin};
19 | @media screen and (max-width: 420px) {
20 | margin: ${({ responsive }) => responsive && 0};
21 | }
22 | `
23 |
24 | const Text = styled.span`
25 | @media screen and (max-width: 420px) {
26 | display: ${({ responsive }) => responsive && 'none'};
27 | }
28 | `
29 |
30 | const IconLink = ({
31 | height, icon, right, responsive, children, ...props
32 | }) => {
33 | const { palette, reverse } = props
34 | const iconElement = (
35 |
44 | )
45 | return (
46 |
47 | {right || iconElement}
48 | {children}
49 | {right && iconElement}
50 |
51 | )
52 | }
53 |
54 | IconLink.propTypes = {
55 | icon: PropTypes.string.isRequired,
56 | height: PropTypes.number,
57 | palette: PropTypes.string,
58 | reverse: PropTypes.bool,
59 | responsive: PropTypes.bool,
60 | right: PropTypes.bool,
61 | children: PropTypes.node,
62 | }
63 |
64 | export default IconLink
65 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconLink/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { IconLink } from 'components'
4 |
5 | storiesOf('IconLink', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('right', () => (
10 | Hello
11 | ))
12 | .add('inside paragraph', () => (
13 |
14 | Consequat cupidatat id
15 | excepteur
16 | {' '}
17 | ex nisi proident et sunt fugiat id pariatur.
18 |
19 | ))
20 |
--------------------------------------------------------------------------------
/src-example/components/molecules/IconLink/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import IconLink from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('mounts with different combination of props', () => {
8 | mount(test )
9 | mount(test )
10 | mount(test )
11 | mount(test )
12 | mount(test )
13 | mount( )
14 | mount( )
15 | mount( )
16 | mount( )
17 | })
18 |
19 | it('renders children when passed in', () => {
20 | const wrapper = wrap({ children: 'test' })
21 | expect(wrapper.contains('test')).toBe(true)
22 | })
23 |
24 | it('renders props when passed in', () => {
25 | const wrapper = wrap({ id: 'foo' })
26 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
27 | })
28 |
29 | it('renders icon on left by default', () => {
30 | const wrapper = wrap({ children: 'test' })
31 | expect(wrapper.children().at(0).prop('icon')).toBe('github')
32 | })
33 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Modal/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css, injectGlobal } from 'styled-components'
4 | import ReactModal from 'react-modal'
5 | import { font, palette } from 'styled-theme'
6 |
7 | import { Heading, IconButton } from 'components'
8 |
9 | injectGlobal`
10 | body.ReactModal__Body--open {
11 | overflow: hidden;
12 | }
13 | `
14 |
15 | const overlayStyles = css`
16 | position: fixed;
17 | background-color: rgba(0, 0, 0, 0.5);
18 | top: 0;
19 | right: 0;
20 | left: 0;
21 | bottom: 0;
22 | z-index: 9999;
23 | transition: opacity 250ms ease-in-out;
24 | opacity: 0;
25 | &[class*="after-open"] {
26 | opacity: 1;
27 | }
28 | &[class*="before-close"] {
29 | opacity: 0;
30 | }
31 | `
32 |
33 | const ModalBox = styled(ReactModal)`
34 | position: absolute;
35 | display: flex;
36 | flex-direction: column;
37 | font-family: ${font('primary')};
38 | font-size: 1rem;
39 | background-color: ${palette('grayscale', 0, true)};
40 | border-radius: 0.125em;
41 | color: ${palette('grayscale', 0)};
42 | top: calc(50% - 1rem);
43 | left: calc(50% - 1rem);
44 | right: auto;
45 | bottom: auto;
46 | margin: 1rem calc(-50% + 1rem) 1rem 1rem;
47 | transform: translate(-50%, 100%);
48 | transition: transform 250ms ease-in-out;
49 | outline: none;
50 | box-sizing: border-box;
51 | min-width: 320px;
52 | max-width: calc(640px - 1rem);
53 | max-height: calc(100% - 1rem);
54 | padding-top: ${({ hasHeader }) => hasHeader ? 0 : '1rem'};
55 | @media screen and (max-width: 640px) {
56 | width: calc(100% - 1rem);
57 | min-width: 0;
58 | }
59 | &[class*="after-open"] {
60 | transform: translate(-50%, -50%);
61 | }
62 | &[class*="before-close"] {
63 | transform: translate(-50%, 100%);
64 | }
65 | `
66 |
67 | const Header = styled.header`
68 | display: flex;
69 | align-items: center;
70 | padding: 1rem;
71 | > *:first-child {
72 | flex: 1;
73 | }
74 | `
75 |
76 | const StyledHeading = styled(Heading)`
77 | margin: 0 1rem 0 0;
78 | text-overflow: ellipsis;
79 | overflow: hidden;
80 | white-space: nowrap;
81 | `
82 |
83 | const Content = styled.div`
84 | overflow: auto;
85 | padding: 0 1rem;
86 | margin-bottom: 1rem;
87 | `
88 |
89 | const StyledReactModal = styled(({ className, ...props }) => (
90 |
91 | ))`${overlayStyles}`
92 |
93 | const Modal = ({
94 | children, title, closeable, onClose, ...props
95 | }) => {
96 | const { reverse } = props
97 | const hasHeader = title || closeable
98 | return (
99 |
105 | {hasHeader
106 | && (
107 |
108 | {title}
109 | {closeable && }
110 |
111 | )
112 | }
113 |
114 | {children}
115 |
116 |
117 | )
118 | }
119 |
120 | Modal.propTypes = {
121 | children: PropTypes.node,
122 | title: PropTypes.string,
123 | closeable: PropTypes.bool,
124 | reverse: PropTypes.bool,
125 | onClose: PropTypes.func.isRequired,
126 | }
127 |
128 | export default Modal
129 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Modal/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { action } from '@storybook/addon-actions'
4 | import { Modal } from 'components'
5 |
6 | storiesOf('Modal', module)
7 | .add('default', () => (
8 |
9 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
10 |
11 | ))
12 | .add('with title', () => (
13 |
14 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
15 |
16 | ))
17 | .add('closeable', () => (
18 |
19 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
20 |
21 | ))
22 | .add('reverse', () => (
23 |
24 | Ullamco et reprehenderit magna cillum ullamco consectetur et enim aliqua.
25 |
26 | ))
27 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Modal/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import Modal from '.'
4 |
5 | const onClose = jest.fn()
6 |
7 | const wrap = (props = {}) => shallow( )
8 |
9 | it('renders modal with different props', () => {
10 | mount( )
11 | mount( )
12 | })
13 |
14 | it('renders children when passed in', () => {
15 | const wrapper = wrap({ children: 'test' })
16 | expect(wrapper.contains('test')).toBe(true)
17 | })
18 |
19 | it('renders props when passed in', () => {
20 | const wrapper = wrap({ htmlFor: 'foo' })
21 | expect(wrapper.find({ htmlFor: 'foo' })).toHaveLength(1)
22 | })
23 |
24 | it('renders title when passed in', () => {
25 | const wrapper = wrap({ title: 'test title' })
26 | expect(wrapper.contains('test title')).toBe(true)
27 | })
28 |
29 | it('renders close button when closeable is passed in', () => {
30 | const wrapper = wrap({ closeable: true })
31 | expect(wrapper.find({ onClick: onClose })).toHaveLength(1)
32 | })
33 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Molecule/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | const Wrapper = styled.div`
7 | font-family: ${font('primary')};
8 | color: ${palette('grayscale', 0)};
9 | `
10 |
11 | const Molecule = ({ children, ...props }) => {
12 | return (
13 |
14 | {children}
15 |
16 | )
17 | }
18 |
19 | Molecule.propTypes = {
20 | reverse: PropTypes.bool,
21 | children: PropTypes.node,
22 | }
23 |
24 | export default Molecule
25 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Molecule/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Molecule } from 'components'
4 |
5 | storiesOf('Molecule', module)
6 | .add('default', () => (
7 | Hello
8 | ))
9 | .add('reverse', () => (
10 | Hello
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Molecule/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Molecule from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
--------------------------------------------------------------------------------
/src-example/components/molecules/PrimaryNavigation/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { palette } from 'styled-theme'
5 |
6 | import { Link } from 'components'
7 |
8 | const Nav = styled.nav`
9 | display: flex;
10 | list-style: none;
11 | > :not(:first-child) {
12 | margin-left: 1rem;
13 | }
14 | a {
15 | font-weight: 300;
16 | color: ${palette('grayscale', 2)};
17 | font-size: 1.25rem;
18 | &.active {
19 | color: ${palette('grayscale', 0)};
20 | }
21 | }
22 | `
23 |
24 | const PrimaryNavigation = (props) => {
25 | return (
26 |
27 | Home
28 | Sample page
29 |
30 | )
31 | }
32 |
33 | PrimaryNavigation.propTypes = {
34 | reverse: PropTypes.bool,
35 | }
36 |
37 | export default PrimaryNavigation
38 |
--------------------------------------------------------------------------------
/src-example/components/molecules/PrimaryNavigation/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { PrimaryNavigation } from 'components'
4 |
5 | storiesOf('PrimaryNavigation', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('reverse', () => (
10 |
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/molecules/PrimaryNavigation/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import PrimaryNavigation from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Slider/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 | import { ifProp, prop } from 'styled-tools'
6 |
7 | const fontSize = ({ height }) => `${height / 35.5555555556}rem`
8 |
9 | const thumbColor = ({ disabled }) => palette(disabled ? 2 : 1)
10 |
11 | const barColor = palette('grayscale', 2, true)
12 |
13 | const hoverThumbColor = ({ disabled }) => !disabled && palette(0)
14 |
15 | const thumbHeight = '1.5em'
16 | const thumbWidth = '1.5em'
17 | const barHeight = '0.6em'
18 |
19 | const border = '0.0625em solid transparent'
20 | const borderRadius = '0.125em'
21 |
22 | const Wrapper = styled.div`
23 | font-family: ${font('primary')};
24 | display: flex;
25 | align-items: center;
26 | justify-content: center;
27 | width: 100%;
28 | height: 100%;
29 | font-size: ${fontSize};
30 | color: ${palette('grayscale', 0)};
31 | background-color: transparent;
32 | `
33 |
34 | const Range = styled.input`
35 | -webkit-appearance: none;
36 | margin: 0 0.4375em;
37 | width: 100%;
38 | height: 1rem;
39 | background: transparent;
40 |
41 | &:focus {
42 | outline: none;
43 | }
44 |
45 | ${''/* Thumb */}
46 |
47 | &::-webkit-slider-thumb {
48 | -webkit-appearance: none;
49 | background: ${thumbColor};
50 | border: ${border};
51 | height: ${thumbHeight};
52 | width: ${thumbWidth};
53 | border-radius: ${borderRadius};
54 | cursor: pointer;
55 | margin-top: -0.53em;
56 | transition: background .15s ease-in-out;
57 | &:hover {
58 | background: ${hoverThumbColor};
59 | }
60 | }
61 |
62 | &::-moz-range-thumb {
63 | background: ${thumbColor};
64 | height: ${thumbHeight};
65 | width: ${thumbWidth};
66 | border: ${border};
67 | border-radius: ${borderRadius};
68 | cursor: pointer;
69 | transition: background .15s ease-in-out;
70 | &:hover {
71 | background: ${hoverThumbColor};
72 | }
73 | }
74 |
75 | &::-ms-thumb {
76 | height: ${thumbHeight};
77 | width: ${thumbWidth};
78 | cursor: pointer;
79 | border: ${border};
80 | border-radius: ${borderRadius};
81 | }
82 |
83 | ${''/* Track */}
84 |
85 | &::-webkit-slider-runnable-track {
86 | width: 100%;
87 | height: ${barHeight};
88 | background: ${barColor};
89 | border-radius: ${borderRadius};
90 | border: ${border};
91 | }
92 |
93 | &::-ms-track {
94 | width: 100%;
95 | height: ${barHeight};
96 | border: ${border};
97 | border-radius: ${borderRadius};
98 | color: transparent;
99 | }
100 |
101 | &::-moz-range-track {
102 | width: 100%;
103 | height: ${barHeight};
104 | background: ${barColor};
105 | border-radius: ${borderRadius};
106 | border: ${border};
107 | }
108 |
109 | &::-ms-fill-lower {
110 | background: ${barColor};
111 | border: ${border};
112 | border-radius: ${borderRadius};
113 | }
114 |
115 | &::-ms-fill-upper {
116 | background: ${barColor};
117 | border: ${border};
118 | border-radius: ${borderRadius}
119 | }
120 | `
121 |
122 | const Text = styled.span`
123 | padding: 0.4375em;
124 | @media screen and (max-width: ${prop('breakpoint')}px) {
125 | display: ${ifProp('responsive', 'none !important')};
126 | }
127 | `
128 |
129 | const Slider = ({
130 | id, min, max, defaultValue, step, ...props
131 | }) => {
132 | const { breakpoint, responsive } = props
133 | return (
134 |
135 | {min}
136 |
137 | {max}
138 |
139 | )
140 | }
141 |
142 | Slider.propTypes = {
143 | id: PropTypes.string,
144 | min: PropTypes.number,
145 | max: PropTypes.number,
146 | defaultValue: PropTypes.number,
147 | step: PropTypes.number,
148 | reverse: PropTypes.bool,
149 | disabled: PropTypes.bool,
150 | responsive: PropTypes.bool,
151 | breakpoint: PropTypes.number,
152 | }
153 |
154 | Slider.defaultProps = {
155 | min: 0,
156 | max: 2,
157 | defaultValue: 1,
158 | step: 1,
159 | palette: 'primary',
160 | type: 'range',
161 | breakpoint: 420,
162 | responsive: false,
163 | }
164 |
165 | export default Slider
166 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Slider/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Slider } from 'components'
4 |
5 | storiesOf('Slider', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('reverse', () => (
10 |
11 | ))
12 | .add('disabled', () => (
13 |
14 | ))
15 | .add('responsive with breakpoint', () => (
16 |
17 | ))
18 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Slider/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import Slider from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders input props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
12 | it('mounts with different combination of props', () => {
13 | mount( )
14 | mount( )
15 | mount( )
16 | })
17 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Table/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | import { Caption } from 'components'
7 |
8 | const StyledTable = styled.table`
9 | font-family: ${font('primary')};
10 | border-collapse: collapse;
11 | width: 100%;
12 | border: 1px solid ${palette('grayscale', 1, true)};
13 | color: ${palette('grayscale', 0)};
14 | `
15 |
16 | const Table = ({
17 | caption, head, foot, children, ...props
18 | }) => {
19 | const { reverse } = props
20 | return (
21 |
22 | {caption && {caption} }
23 | {head && {head} }
24 | {foot && {foot} }
25 | {children}
26 |
27 | )
28 | }
29 |
30 | Table.propTypes = {
31 | caption: PropTypes.string,
32 | head: PropTypes.node,
33 | foot: PropTypes.node,
34 | children: PropTypes.any,
35 | reverse: PropTypes.bool,
36 | }
37 |
38 | export default Table
39 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Table/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Table } from 'components'
4 |
5 | storiesOf('Table', module)
6 | .add('default', () => (
7 |
8 |
9 | Cell 1
10 | Cell 2
11 | Cell 3
12 |
13 |
14 | Cell 1
15 | Cell 2
16 | Cell 3
17 |
18 |
19 | ))
20 | .add('with head', () => (
21 |
24 | Heading 1
25 | Heading 2
26 | Heading 3
27 |
28 | )}
29 | >
30 |
31 | Cell 1
32 | Cell 2
33 | Cell 3
34 |
35 |
36 | Cell 1
37 | Cell 2
38 | Cell 3
39 |
40 |
41 | ))
42 | .add('with foot', () => (
43 |
46 | Footer 1
47 | Footer 2
48 | Footer 3
49 |
50 | )}
51 | >
52 |
53 | Cell 1
54 | Cell 2
55 | Cell 3
56 |
57 |
58 | Cell 1
59 | Cell 2
60 | Cell 3
61 |
62 |
63 | ))
64 | .add('with caption', () => (
65 |
66 |
67 | Cell 1
68 | Cell 2
69 | Cell 3
70 |
71 |
72 | Cell 1
73 | Cell 2
74 | Cell 3
75 |
76 |
77 | ))
78 |
--------------------------------------------------------------------------------
/src-example/components/molecules/Table/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Table from '.'
4 |
5 | const wrap = (props = {}) => shallow()
6 |
7 | it('renders children when passed in', () => {
8 | const wrapper = wrap({ children: 'test' })
9 | expect(wrapper.contains('test')).toBe(true)
10 | })
11 |
12 | it('renders props when passed in', () => {
13 | const wrapper = wrap({ id: 'foo' })
14 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
15 | })
16 |
17 | it('renders caption when passed in', () => {
18 | const wrapper = wrap({ caption: 'test caption' })
19 | expect(wrapper.contains('test caption')).toBe(true)
20 | })
21 |
22 | it('renders head when passed in', () => {
23 | const wrapper = wrap({ head: 'test head' })
24 | expect(wrapper.contains('test head')).toBe(true)
25 | })
26 |
27 | it('renders foot when passed in', () => {
28 | const wrapper = wrap({ foot: 'test foot' })
29 | expect(wrapper.contains('test foot')).toBe(true)
30 | })
31 |
--------------------------------------------------------------------------------
/src-example/components/organisms/FeatureList/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import {
5 | Feature, Link, Heading, Paragraph,
6 | } from 'components'
7 |
8 | const Grid = styled.div`
9 | display: flex;
10 | flex-flow: row wrap;
11 | > * {
12 | width: calc(50% - 2rem);
13 | @media screen and (max-width: 640px) {
14 | width: 100%;
15 | }
16 | }
17 | `
18 |
19 | const StyledHeading = styled(Heading)`
20 | text-align: center;
21 | `
22 |
23 | const Description = styled(Paragraph)`
24 | text-align: center;
25 | margin: 2rem;
26 | @media screen and (max-width: 640px) {
27 | margin: 1rem;
28 | }
29 | `
30 |
31 | const StyledFeature = styled(Feature)`
32 | margin: 1rem;
33 | @media screen and (max-width: 640px) {
34 | margin: 0;
35 | }
36 | `
37 |
38 | const FeatureList = ({ ...props }) => (
39 |
40 | Basic stack
41 |
42 | It includes everything necessary to build a typical web app with focus on productivity and developer experience.
43 |
44 | Learn more about the recommended workflow
45 |
46 |
47 |
53 | The Facebook's JavaScript library for building user interfaces using components.
54 |
55 |
61 | The most popular declarative routing library for React and React Native.
62 |
63 |
69 | The awesome module bundler with
70 | {' '}
71 | Hot Module Replacement
72 | {' '}
73 | enabled.
74 |
75 |
81 | The great testing framework used by Facebook to test all their Javascript code.
82 |
83 |
84 | Optional features
85 |
86 | Features separated into another branches so you can use them only if you need to.
87 |
88 |
89 |
95 | The predictable state container for JavaScript apps.
96 |
97 |
103 | Write once and run on both server and client.
104 |
105 |
106 |
107 | )
108 |
109 | export default FeatureList
110 |
--------------------------------------------------------------------------------
/src-example/components/organisms/FeatureList/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { FeatureList } from 'components'
4 |
5 | storiesOf('FeatureList', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/organisms/FeatureList/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import FeatureList from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Footer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { palette } from 'styled-theme'
4 |
5 | import { Paragraph, Link, Icon } from 'components'
6 |
7 | const Wrapper = styled.div`
8 | background-color: ${palette('grayscale', 1, true)};
9 | padding: 2rem;
10 | `
11 |
12 | const Credits = styled(Paragraph)`
13 | vertical-align: center;
14 | text-align: center;
15 | margin: 0;
16 | `
17 |
18 | const Footer = (props) => {
19 | return (
20 |
21 |
22 | Made with
23 | {' '}
24 |
25 | {' '}
26 | by
27 | {' '}
28 | Haz
29 |
30 |
31 | )
32 | }
33 |
34 | export default Footer
35 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Footer/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Footer } from 'components'
4 |
5 | storiesOf('Footer', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Footer/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Footer from '.'
4 |
5 | const wrap = (props = {}) => shallow().dive()
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { size } from 'styled-theme'
4 |
5 | import { IconLink, PrimaryNavigation, Block } from 'components'
6 |
7 | const Wrapper = styled(Block)`
8 | display: flex;
9 | justify-content: center;
10 | padding: 1rem;
11 | @media screen and (max-width: 640px) {
12 | padding: 0.5rem;
13 | }
14 | `
15 |
16 | const InnerWrapper = styled.div`
17 | display: flex;
18 | align-items: center;
19 | width: 100%;
20 | max-width: ${size('maxWidth')};
21 | > :not(:first-child) {
22 | margin-left: 1rem;
23 | }
24 | `
25 |
26 | const Header = (props) => {
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Header
38 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Header/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Header } from 'components'
4 |
5 | storiesOf('Header', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Header/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Header from '.'
4 |
5 | const wrap = (props = {}) => shallow().dive()
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Hero/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { palette, size } from 'styled-theme'
4 |
5 | import {
6 | Block,
7 | Paragraph,
8 | IconLink,
9 | IconButton,
10 | LogoImage,
11 | PreformattedText,
12 | Heading,
13 | Tooltip,
14 | } from 'components'
15 |
16 | const Wrapper = styled(Block)`
17 | display: flex;
18 | justify-content: center;
19 | padding: 2rem;
20 | box-sizing: border-box;
21 | @media screen and (max-width: 640px) {
22 | padding-left: 0.25rem;
23 | padding-right: 0.25rem;
24 | }
25 | `
26 |
27 | const InnerWrapper = styled.div`
28 | display: flex;
29 | width: 100%;
30 | max-width: ${size('maxWidth')};
31 | @media screen and (max-width: 640px) {
32 | flex-wrap: wrap;
33 | }
34 | `
35 |
36 | const Section = styled.section`
37 | display: flex;
38 | flex-direction: column;
39 | align-items: center;
40 | padding: 2rem;
41 | box-sizing: border-box;
42 | &:first-child {
43 | flex: none;
44 | }
45 | @media screen and (max-width: 640px) {
46 | padding: 0.25rem;
47 | width: 100%;
48 | }
49 | `
50 |
51 | const Text = styled(Paragraph)`
52 | color: ${palette('grayscale', 3, true)};
53 | font-weight: 300;
54 | font-size: 1.35rem;
55 | line-height: 1.35em;
56 | width: 100%;
57 | letter-spacing: 0.05em;
58 | @media screen and (max-width: 640px) {
59 | text-align: center;
60 | font-size: 1rem;
61 | }
62 | `
63 |
64 | const ButtonGroup = styled.div`
65 | margin-top: 2rem;
66 | display: flex;
67 | > :not(:first-child) {
68 | margin-left: 0.5rem;
69 | }
70 | `
71 |
72 | const Instructions = styled.div`
73 | width: 100%;
74 | margin-top: 2rem;
75 | @media screen and (max-width: 640px) {
76 | margin-top: 1rem;
77 | }
78 | `
79 |
80 | const Hero = (props) => {
81 | return (
82 |
83 |
84 |
85 |
86 |
87 |
88 | GitHub
89 |
90 |
91 | Docs
92 |
93 |
94 |
95 |
96 |
97 | ARc
98 | {' '}
99 | is a
100 | React
101 | {' '}
102 | starter kit based on the
103 | Atomic Design
104 | {' '}
105 | methodology. It's
106 | progressive
107 | , which means that you can start with the basic boilerplate and try the other features when you are comfortable.
108 |
109 |
110 | Install
111 |
112 | git clone -b master https://github.com/diegohaz/arc my-app
113 |
114 |
120 | Learn more
121 |
122 |
123 |
124 |
125 |
126 | )
127 | }
128 |
129 | export default Hero
130 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Hero/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Hero } from 'components'
4 |
5 | storiesOf('Hero', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Hero/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Hero from '.'
4 |
5 | const wrap = (props = {}) => shallow( ).dive()
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Organism/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { font, palette } from 'styled-theme'
5 |
6 | const Wrapper = styled.div`
7 | font-family: ${font('primary')};
8 | color: ${palette('grayscale', 0)};
9 | `
10 |
11 | const Organism = (props) => {
12 | return (
13 | content
14 | )
15 | }
16 |
17 | Organism.propTypes = {
18 | reverse: PropTypes.bool,
19 | }
20 |
21 | export default Organism
22 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Organism/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { Organism } from 'components'
4 |
5 | storiesOf('Organism', module)
6 | .add('default', () => (
7 |
8 | ))
9 | .add('reverse', () => (
10 |
11 | ))
12 |
--------------------------------------------------------------------------------
/src-example/components/organisms/Organism/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import Organism from '.'
4 |
5 | const wrap = (props = {}) => shallow( )
6 |
7 | it('renders props when passed in', () => {
8 | const wrapper = wrap({ id: 'foo' })
9 | expect(wrapper.find({ id: 'foo' })).toHaveLength(1)
10 | })
11 |
--------------------------------------------------------------------------------
/src-example/components/pages/GenericPage/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const GenericPage = () => {
4 | return (
5 | Generic Page
6 | )
7 | }
8 |
9 | export default GenericPage
10 |
--------------------------------------------------------------------------------
/src-example/components/pages/GenericPage/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { GenericPage } from 'components'
4 |
5 | storiesOf('GenericPage', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/pages/GenericPage/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import GenericPage from '.'
4 |
5 | it('renders', () => {
6 | shallow( )
7 | })
8 |
--------------------------------------------------------------------------------
/src-example/components/pages/HomePage/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design
2 | import React from 'react'
3 |
4 | import {
5 | PageTemplate, Header, Hero, Footer, FeatureList,
6 | } from 'components'
7 |
8 | const HomePage = () => {
9 | return (
10 | }
12 | hero={ }
13 | footer={}
14 | >
15 |
16 |
17 | )
18 | }
19 |
20 | export default HomePage
21 |
--------------------------------------------------------------------------------
/src-example/components/pages/HomePage/index.stories.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Storybook
2 | import React from 'react'
3 | import { storiesOf } from '@storybook/react'
4 | import { HomePage } from 'components'
5 |
6 | storiesOf('HomePage', module)
7 | .add('default', () => (
8 |
9 | ))
10 |
--------------------------------------------------------------------------------
/src-example/components/pages/HomePage/index.test.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Testing-components
2 | import React from 'react'
3 | import { shallow } from 'enzyme'
4 | import HomePage from '.'
5 |
6 | it('renders', () => {
7 | shallow( )
8 | })
9 |
--------------------------------------------------------------------------------
/src-example/components/pages/NotFoundPage/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {
4 | PageTemplate, Header, Footer, Heading,
5 | } from 'components'
6 |
7 | const NotFoundPage = () => {
8 | return (
9 | } footer={}>
10 | 404 Not Found
11 |
12 | )
13 | }
14 |
15 | export default NotFoundPage
16 |
--------------------------------------------------------------------------------
/src-example/components/pages/NotFoundPage/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { NotFoundPage } from 'components'
4 |
5 | storiesOf('NotFoundPage', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/pages/NotFoundPage/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import NotFoundPage from '.'
4 |
5 | it('renders', () => {
6 | shallow( )
7 | })
8 |
--------------------------------------------------------------------------------
/src-example/components/pages/SamplePage/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {
4 | PageTemplate, Header, Footer, Paragraph, Heading,
5 | } from 'components'
6 |
7 | const SamplePage = () => {
8 | return (
9 | } footer={}>
10 | Irure mollit aute tempor incididunt eiusmod fugiat tempor aute ex sit aute est proident est.
11 |
12 | Do cillum nulla consectetur excepteur aliquip adipisicing ipsum. Consectetur voluptate cillum cillum fugiat adipisicing eiusmod incididunt ut voluptate do aliquip ad irure occaecat cupidatat quis. Laborum laborum id quis officia anim quis in anim eu et aliquip sunt do excepteur. Consectetur ullamco sint do do nostrud tempor labore laboris sit fugiat veniam reprehenderit.
13 |
14 |
15 | )
16 | }
17 |
18 | export default SamplePage
19 |
--------------------------------------------------------------------------------
/src-example/components/pages/SamplePage/index.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { SamplePage } from 'components'
4 |
5 | storiesOf('SamplePage', module)
6 | .add('default', () => (
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src-example/components/pages/SamplePage/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import SamplePage from '.'
4 |
5 | it('renders', () => {
6 | shallow( )
7 | })
8 |
--------------------------------------------------------------------------------
/src-example/components/templates/GenericTemplate/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | const Wrapper = styled.div`
6 | display: flex;
7 | flex-direction: column;
8 | padding-top: 3.75rem;
9 | min-height: 100vh;
10 | box-sizing: border-box;
11 | `
12 |
13 | const Content = styled.section`
14 | width: 100%;
15 | box-sizing: border-box;
16 | margin: 2rem auto;
17 | max-width: 920px;
18 | `
19 |
20 | const GenericTemplate = ({ children, ...props }) => {
21 | return (
22 |
23 | {children}
24 |
25 | )
26 | }
27 |
28 | GenericTemplate.propTypes = {
29 | children: PropTypes.any.isRequired,
30 | }
31 |
32 | export default GenericTemplate
33 |
--------------------------------------------------------------------------------
/src-example/components/templates/GenericTemplate/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import GenericTemplate from '.'
4 |
5 | const wrap = (props = {}) => shallow(test )
6 |
7 | it('mounts', () => {
8 | mount(test )
9 | })
10 |
11 | it('renders children when passed in', () => {
12 | const wrapper = wrap()
13 | expect(wrapper.contains('test')).toBe(true)
14 | })
15 |
--------------------------------------------------------------------------------
/src-example/components/templates/PageTemplate/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design#templates
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 | import styled from 'styled-components'
5 | import { size } from 'styled-theme'
6 |
7 | const Wrapper = styled.div`
8 | display: flex;
9 | flex-direction: column;
10 | padding-top: 3.75rem;
11 | min-height: 100vh;
12 | box-sizing: border-box;
13 | @media screen and (max-width: 640px) {
14 | padding-top: 3.25rem;
15 | }
16 | `
17 |
18 | const Header = styled.header`
19 | position: fixed;
20 | top: 0;
21 | width: 100%;
22 | z-index: 999;
23 | `
24 |
25 | const Hero = styled.section``
26 |
27 | const Sponsor = styled.section``
28 |
29 | const Content = styled.section`
30 | width: 100%;
31 | box-sizing: border-box;
32 | margin: 2rem auto;
33 | max-width: ${size('maxWidth')};
34 | `
35 |
36 | const Footer = styled.footer`
37 | margin-top: auto;
38 | `
39 |
40 | const PageTemplate = ({
41 | header, hero, sponsor, children, footer, ...props
42 | }) => {
43 | return (
44 |
45 |
46 | {hero && {hero} }
47 | {sponsor && {sponsor} }
48 | {children}
49 |
50 |
51 | )
52 | }
53 |
54 | PageTemplate.propTypes = {
55 | header: PropTypes.node.isRequired,
56 | hero: PropTypes.node,
57 | sponsor: PropTypes.node,
58 | footer: PropTypes.node.isRequired,
59 | children: PropTypes.any.isRequired,
60 | }
61 |
62 | export default PageTemplate
63 |
--------------------------------------------------------------------------------
/src-example/components/templates/PageTemplate/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mount, shallow } from 'enzyme'
3 | import PageTemplate from '.'
4 |
5 | const wrap = (props = {}) => (
6 | shallow(test )
7 | )
8 |
9 | it('mounts', () => {
10 | mount(test )
11 | })
12 |
13 | it('renders children when passed in', () => {
14 | const wrapper = wrap()
15 | expect(wrapper.contains('test')).toBe(true)
16 | })
17 |
18 | it('renders header', () => {
19 | const wrapper = wrap()
20 | expect(wrapper.contains('header')).toBe(true)
21 | })
22 |
23 | it('renders hero', () => {
24 | const wrapper = wrap({ hero: 'hero' })
25 | expect(wrapper.contains('hero')).toBe(true)
26 | })
27 |
28 | it('renders sponsor', () => {
29 | const wrapper = wrap({ sponsor: 'sponsor' })
30 | expect(wrapper.contains('sponsor')).toBe(true)
31 | })
32 |
33 | it('renders footer', () => {
34 | const wrapper = wrap()
35 | expect(wrapper.contains('footer')).toBe(true)
36 | })
37 |
--------------------------------------------------------------------------------
/src-example/components/themes/default.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Styling
2 | import { reversePalette } from 'styled-theme/composer'
3 |
4 | const theme = {}
5 |
6 | theme.palette = {
7 | primary: ['#1976d2', '#2196f3', '#71bcf7', '#c2e2fb'],
8 | secondary: ['#c2185b', '#e91e63', '#f06292', '#f8bbd0'],
9 | danger: ['#d32f2f', '#f44336', '#f8877f', '#ffcdd2'],
10 | alert: ['#ffa000', '#ffc107', '#ffd761', '#ffecb3'],
11 | success: ['#388e3c', '#4caf50', '#7cc47f', '#c8e6c9'],
12 | white: ['#fff', '#fff', '#eee'],
13 | grayscale: [
14 | '#212121',
15 | '#414141',
16 | '#616161',
17 | '#9e9e9e',
18 | '#bdbdbd',
19 | '#e0e0e0',
20 | '#eeeeee',
21 | '#ffffff',
22 | ],
23 | }
24 |
25 | theme.reversePalette = reversePalette(theme.palette)
26 |
27 | theme.fonts = {
28 | primary: 'Helvetica Neue, Helvetica, Roboto, sans-serif',
29 | pre: 'Consolas, Liberation Mono, Menlo, Courier, monospace',
30 | quote: 'Georgia, serif',
31 | }
32 |
33 | theme.sizes = {
34 | maxWidth: '1100px',
35 | }
36 |
37 | export default theme
38 |
--------------------------------------------------------------------------------
/src-example/config.js:
--------------------------------------------------------------------------------
1 | const merge = require('lodash/merge')
2 |
3 | const config = {
4 | all: {
5 | env: process.env.NODE_ENV || 'development',
6 | isDev: process.env.NODE_ENV !== 'production',
7 | basename: process.env.PUBLIC_PATH,
8 | isBrowser: typeof window !== 'undefined',
9 | },
10 | test: {},
11 | development: {},
12 | production: {},
13 | }
14 |
15 | module.exports = merge(config.all, config[config.all.env])
16 |
--------------------------------------------------------------------------------
/src-example/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Example-app
2 | import 'react-hot-loader/patch'
3 | import React from 'react'
4 | import { render } from 'react-dom'
5 | import { BrowserRouter } from 'react-router-dom'
6 |
7 | import { basename } from 'config'
8 | import App from 'components/App'
9 |
10 | const renderApp = () => (
11 |
12 |
13 |
14 | )
15 |
16 | const root = document.getElementById('app')
17 | render(renderApp(), root)
18 |
19 | if (module.hot) {
20 | module.hot.accept('components/App', () => {
21 | require('components/App')
22 | render(renderApp(), root)
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route } from 'react-router-dom'
3 | import { injectGlobal, ThemeProvider } from 'styled-components'
4 |
5 | import { HomePage } from 'components'
6 |
7 | // https://github.com/diegohaz/arc/wiki/Styling
8 | import theme from './themes/default'
9 |
10 | injectGlobal`
11 | body {
12 | margin: 0;
13 | }
14 | `
15 |
16 | const App = () => {
17 | return (
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default App
27 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design#do-not-worry
2 | const req = require.context('.', true, /\.\/[^/]+\/[^/]+\/index\.js$/)
3 |
4 | req.keys().forEach((key) => {
5 | const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, '$1')
6 | module.exports[componentName] = req(key).default
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/pages/HomePage/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design
2 | import React from 'react'
3 |
4 | const HomePage = () => {
5 | return (
6 | Hello World
7 | )
8 | }
9 |
10 | export default HomePage
11 |
--------------------------------------------------------------------------------
/src/components/pages/HomePage/index.stories.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Storybook
2 | import React from 'react'
3 | import { storiesOf } from '@storybook/react'
4 | import { HomePage } from 'components'
5 |
6 | storiesOf('HomePage', module)
7 | .add('default', () => (
8 |
9 | ))
10 |
--------------------------------------------------------------------------------
/src/components/pages/HomePage/index.test.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Testing-components
2 | import React from 'react'
3 | import { shallow } from 'enzyme'
4 | import HomePage from '.'
5 |
6 | it('renders', () => {
7 | shallow( )
8 | })
9 |
--------------------------------------------------------------------------------
/src/components/themes/default.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Styling
2 | import { reversePalette } from 'styled-theme/composer'
3 |
4 | const theme = {}
5 |
6 | theme.palette = {
7 | primary: ['#1976d2', '#2196f3', '#71bcf7', '#c2e2fb'],
8 | secondary: ['#c2185b', '#e91e63', '#f06292', '#f8bbd0'],
9 | danger: ['#d32f2f', '#f44336', '#f8877f', '#ffcdd2'],
10 | alert: ['#ffa000', '#ffc107', '#ffd761', '#ffecb3'],
11 | success: ['#388e3c', '#4caf50', '#7cc47f', '#c8e6c9'],
12 | white: ['#fff', '#fff', '#eee'],
13 | grayscale: [
14 | '#212121',
15 | '#414141',
16 | '#616161',
17 | '#9e9e9e',
18 | '#bdbdbd',
19 | '#e0e0e0',
20 | '#eeeeee',
21 | '#ffffff',
22 | ],
23 | }
24 |
25 | theme.reversePalette = reversePalette(theme.palette)
26 |
27 | theme.fonts = {
28 | primary: 'Helvetica Neue, Helvetica, Roboto, sans-serif',
29 | pre: 'Consolas, Liberation Mono, Menlo, Courier, monospace',
30 | quote: 'Georgia, serif',
31 | }
32 |
33 | theme.sizes = {
34 | maxWidth: '1100px',
35 | }
36 |
37 | export default theme
38 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const merge = require('lodash/merge')
2 |
3 | const config = {
4 | all: {
5 | env: process.env.NODE_ENV || 'development',
6 | isDev: process.env.NODE_ENV !== 'production',
7 | basename: process.env.PUBLIC_PATH,
8 | isBrowser: typeof window !== 'undefined',
9 | },
10 | test: {},
11 | development: {},
12 | production: {},
13 | }
14 |
15 | module.exports = merge(config.all, config[config.all.env])
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'react-hot-loader/patch'
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import { BrowserRouter } from 'react-router-dom'
5 |
6 | import { basename } from 'config'
7 | import App from 'components/App'
8 |
9 | const renderApp = () => (
10 |
11 |
12 |
13 | )
14 |
15 | const root = document.getElementById('app')
16 | render(renderApp(), root)
17 |
18 | if (module.hot) {
19 | module.hot.accept('components/App', () => {
20 | require('components/App')
21 | render(renderApp(), root)
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Webpack
2 | const path = require('path')
3 | const webpack = require('webpack')
4 | const HtmlWebpackPlugin = require('html-webpack-plugin')
5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
6 | const WebpackMd5Hash = require('webpack-md5-hash')
7 | const HappyPack = require('happypack')
8 | const mergeWith = require('lodash/mergeWith')
9 | const isArray = require('lodash/isArray')
10 |
11 |
12 | const host = process.env.HOST || 'localhost'
13 | const port = process.env.PORT || 3000
14 | const sourceDir = process.env.SOURCE || 'src'
15 | const publicPath = `/${process.env.PUBLIC_PATH || ''}/`.replace('//', '/')
16 | const sourcePath = path.join(process.cwd(), sourceDir)
17 | const outputPath = path.join(process.cwd(), 'dist')
18 |
19 | function customizer(objValue, srcValue) {
20 | if (isArray(objValue)) {
21 | return objValue.concat(srcValue)
22 | }
23 | return undefined
24 | }
25 |
26 | const wpConfig = {
27 | base: {
28 | module: {
29 | rules: [
30 | { test: /\.jsx?$/, exclude: /node_modules/, use: 'happypack/loader' },
31 | { test: /\.(png|jpe?g|svg|woff2?|ttf|eot)$/, loader: 'url-loader?limit=8000' },
32 | ],
33 | },
34 | plugins: [
35 | new webpack.ProgressPlugin(),
36 | new HtmlWebpackPlugin({
37 | filename: 'index.html',
38 | template: path.join(process.cwd(), 'public/index.html'),
39 | }),
40 | new HappyPack({
41 | loaders: ['babel-loader'],
42 | }),
43 | new webpack.DefinePlugin({
44 | NODE_ENV: process.env.NODE_ENV,
45 | PUBLIC_PATH: publicPath.replace(/\/$/, ''),
46 | }),
47 | ],
48 | resolve: {
49 | extensions: ['.js', '.jsx', '.json'],
50 | modules: [].concat(sourceDir, ['node_modules']),
51 | },
52 | entry: {
53 | app: [sourcePath],
54 | },
55 | output: {
56 | filename: '[name].js',
57 | path: outputPath,
58 | publicPath,
59 | },
60 | },
61 | development: {
62 | mode: 'development',
63 | plugins: [
64 | new webpack.HotModuleReplacementPlugin({
65 | fullBuildTimeout: 200,
66 | }),
67 | ],
68 | entry: {
69 | app: ['webpack/hot/only-dev-server'],
70 | },
71 | devtool: 'cheap-module-source-map',
72 | devServer: {
73 | hot: true,
74 | historyApiFallback: { index: publicPath },
75 | inline: true,
76 | contentBase: 'public',
77 | headers: { 'Access-Control-Allow-Origin': '*' },
78 | host,
79 | port,
80 | stats: 'errors-only',
81 | },
82 | optimization: {
83 | namedModules: true,
84 | },
85 | },
86 | production: {
87 | mode: 'production',
88 | plugins: [
89 | new WebpackMd5Hash(),
90 | ],
91 | output: {
92 | filename: '[name].[chunkhash].js',
93 | },
94 | optimization: {
95 | minimizer: [
96 | new UglifyJsPlugin(),
97 | ],
98 | splitChunks: {
99 | name: 'vendor',
100 | minChunks: 2,
101 | },
102 | },
103 | },
104 | }
105 |
106 | const config = mergeWith(
107 | {},
108 | wpConfig.base,
109 | wpConfig[process.env.NODE_ENV],
110 | customizer,
111 | )
112 |
113 | module.exports = config
114 |
--------------------------------------------------------------------------------