├── .gitignore ├── .storybook ├── addons.js ├── config.js └── preview-head.html ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json └── src ├── App.js ├── App.test.js ├── components ├── Button │ ├── Button.js │ └── Button.stories.js ├── Card │ ├── Card.js │ └── Card.stories.js ├── CardList │ ├── CardLIst.stories.js │ └── CardList.js ├── Footer │ ├── Footer.js │ └── Footer.stories.js ├── Header │ ├── Header.js │ └── Header.stories.js ├── Hero │ ├── Hero.js │ └── Hero.stories.js ├── InlineForm │ ├── InlineForm.js │ └── InlineForm.stories.js ├── Logo │ ├── Logo.js │ └── Logo.stories.js ├── PrimaryNav │ ├── PrimaryNav.js │ └── PrimaryNav.stories.js ├── Section │ ├── Section.js │ └── Section.stories.js └── TextPassage │ ├── TextPassage.js │ └── TextPassage.stories.js ├── css ├── scss │ ├── abstracts │ │ ├── _mixins.scss │ │ └── _variables.scss │ ├── base │ │ ├── _body.scss │ │ ├── _buttons.scss │ │ ├── _forms.scss │ │ ├── _headings.scss │ │ ├── _links.scss │ │ ├── _lists.scss │ │ ├── _main.scss │ │ ├── _media.scss │ │ ├── _reset.scss │ │ ├── _table.scss │ │ └── _text.scss │ ├── components │ │ ├── _buttons.scss │ │ ├── _card-list.scss │ │ ├── _card.scss │ │ ├── _field.scss │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _hero.scss │ │ ├── _icon.scss │ │ ├── _inline-form.scss │ │ ├── _logo.scss │ │ ├── _page-header.scss │ │ ├── _primary-nav.scss │ │ ├── _section.scss │ │ └── _text-passage.scss │ ├── layout │ │ └── _layout.scss │ └── utilities │ │ └── _visibility.scss ├── style.css ├── style.css.map └── style.scss ├── data └── globals.json ├── images ├── fpo-1200x650.png ├── fpo-120x60.png └── fpo-500x300.png ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | *.scssc 24 | 25 | .sass-cache -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | import '@storybook/addon-knobs/register'; 4 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | import '../src/css/style.css'; 3 | 4 | const req = require.context("../src/components", true, /.stories.js$/); 5 | function loadStories() { 6 | req.keys().forEach(filename => req(filename)); 7 | } 8 | 9 | configure(loadStories, module); -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradfrost/dumb-react-sass/2943d080ef8c8cabae87605991f720562fbb7229/.storybook/preview-head.html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dumb React Sass 2 | Dumb React is a collection of [React](https://reactjs.org/) components used to create a static (dumb) website screen. Why do this? Many React tutorials or boilerplates I've encountered are either too basic ("here's a button component!") or more often too complex ("here's a simple simple babel typescript redux cssmodules isometric oh god oh god oh god..."). I wanted to be able to be able to draw a straight line from how a simple component ("[atom](http://atomicdesign.bradfrost.com/chapter-2/#atoms)" in atomic design speak) makes its way into a full [page](http://atomicdesign.bradfrost.com/chapter-2/#pages). 3 | 4 | There are a ton of different ways to build reusable components and put dynamic content inside them, and many teams — even ones that aren't building highly-interactive web apps that would actually benefit from a tool like React — are reaching for React to create component-driven experiences. So in that spirit, I wanted to create a demo that shows how to construct a whole screen (even if it's dumb, static one) one out of React components. 5 | 6 | # React w/ Storybook Starter 7 | This repo is a fork of Micah Godbolt's [React with Storybook Starter](https://github.com/micahgodbolt/react-with-storybook-starter), which is a combination of [Create React App](https://github.com/facebook/create-react-app) and the [Storybook CLI](https://github.com/storybooks/storybook#getting-started). The `stories` folder has been changed to `components` and a new Button component has been scaffolded and used in the application. 8 | 9 | ## Using This Repo 10 | 11 | - `git clone https://github.com/bradfrost/dumb-react.git && cd dumb-react` 12 | - `npm install` 13 | - `npm start` will start the application 14 | - `npm run storybook` will start the storybook. 15 | 16 | ## Building components 17 | 18 | Start building in the `src/components` folder with this folder structure 19 | 20 | ```js 21 | - ComponentName 22 | - ComponentName.stories.js 23 | - ComponentName.js 24 | ``` 25 | 26 | Create `src/components/Button` and add `Button.css`, `Button.js` and `Button.stories.js` 27 | 28 | __Button.js__ will be: 29 | 30 | ```js 31 | import React, { Component } from 'react'; 32 | import './Button.scss'; 33 | 34 | export class Button extends Component { 35 | render() { 36 | return ( 37 | 38 | ); 39 | } 40 | } 41 | ``` 42 | 43 | __Button.stories.js__ will be: 44 | 45 | ```js 46 | import React from 'react'; 47 | import { storiesOf } from '@storybook/react'; 48 | import { Button } from './Button'; 49 | 50 | let stories = storiesOf('Button', module); 51 | 52 | stories.add('Default', () => 53 | 56 | ); 57 | 58 | ``` 59 | 60 | __Button.css__ is plain CSS, but will automatically be loaded when the component is used. 61 | 62 | ### Run Storybook 63 | 64 | ```bash 65 | npm run storybook 66 | ``` 67 | 68 | ## Add Button to App.js 69 | 70 | ```jsx 71 | import React, { Component } from 'react'; 72 | import { Button } from './components/Button/Button'; 73 | 74 | class App extends Component { 75 | render() { 76 | return ( 77 |
78 | 79 |
80 | ); 81 | } 82 | } 83 | 84 | export default App; 85 | ``` 86 | 87 | ### Run the application 88 | 89 | ```bash 90 | npm start 91 | ``` 92 | 93 | ## Adding Sass 94 | 95 | Adding Sass involves "ejecting" out of create react app. This process is out of the scope of this demo, but I'll include some links below. 96 | 97 | - [Adding Sass support to Create React App](https://medium.com/front-end-hacking/how-to-add-sass-or-scss-to-create-react-app-c303dae4b5bc) 98 | - [Adding Sass support to Storybook](https://storybook.js.org/configurations/custom-webpack-config/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dumb-react-sass", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "prop-types": "^15.6.2", 7 | "react": "^16.6.0", 8 | "react-dom": "^16.6.0", 9 | "react-scripts": "1.1.4" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test --env=jsdom", 15 | "eject": "react-scripts eject", 16 | "scss": "node-sass --output-style compressed -o src/css src/css", 17 | "watch:css": "onchange 'src/css/**/*.scss' -- npm run scss", 18 | "storybook": "start-storybook -p 9009 -s public", 19 | "build-storybook": "build-storybook -s public" 20 | }, 21 | "devDependencies": { 22 | "@storybook/addon-actions": "^3.4.11", 23 | "@storybook/addon-knobs": "^3.4.11", 24 | "@storybook/addon-links": "^3.4.11", 25 | "@storybook/addons": "^3.4.11", 26 | "@storybook/react": "^3.4.11", 27 | "babel-core": "^6.26.3", 28 | "babel-runtime": "^6.26.0", 29 | "classnames": "^2.2.6", 30 | "css-loader": "^0.28.11", 31 | "node-sass": "^4.10.0", 32 | "onchange": "^5.1.0", 33 | "sass-loader": "^7.1.0", 34 | "storybook-addon-smart-knobs": "^3.3.1", 35 | "style-loader": "^0.21.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradfrost/dumb-react-sass/2943d080ef8c8cabae87605991f720562fbb7229/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './css/style.css'; 3 | import { Header } from './components/Header/Header'; 4 | import { Hero } from './components/Hero/Hero'; 5 | import { Section } from './components/Section/Section'; 6 | import { CardList } from './components/CardList/CardList'; 7 | import { TextPassage } from './components/TextPassage/TextPassage'; 8 | import { Footer } from './components/Footer/Footer'; 9 | 10 | import heroImg from './images/fpo-1200x650.png'; 11 | 12 | class App extends Component { 13 | render() { 14 | return ( 15 |
16 |
17 | 18 | 19 |
20 | 40 | 41 |
42 | 43 |
44 |
45 | 46 |

A text passage contains arbitrary text that might come from a CMS. It should live within a container that caps the line length of the text to avoid a straining reading experience.

47 | 48 |

Heading 2

49 | 50 |

This is another paragraph of text. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

51 | 52 |
    53 |
  • Here is a unordered list item
  • 54 |
  • Here is a unordered list item
  • 55 |
  • Here is a unordered list item
  • 56 |
  • Here is a unordered list item
  • 57 |
58 | 59 |

Heading 3

60 | 61 |
    62 |
  1. Here is a unordered list item
  2. 63 |
  3. Here is a unordered list item
  4. 64 |
  5. Here is a unordered list item
  6. 65 |
  7. Here is a unordered list item
  8. 66 |
67 | 68 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

69 | 70 |
71 |

This is a quotation from something.

72 | Cite source 73 |
74 | 75 |

That is all.

76 |
77 |
{/* end l-linelength-container */} 78 |
79 |
81 | ); 82 | } 83 | } 84 | 85 | export default App; 86 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import classnames from "classnames"; 3 | import PropTypes from 'prop-types'; 4 | 5 | export class Button extends Component { 6 | 7 | render() { 8 | const btnClass = classnames('c-btn', this.props.className, { 9 | 'c-btn--secondary': this.props.issecondary 10 | }); 11 | 12 | return ( 13 | 20 | ); 21 | } 22 | } 23 | 24 | Button.propTypes = { 25 | btnClass: PropTypes.string, 26 | issecondary: PropTypes.bool, 27 | disabled: PropTypes.bool, 28 | text: PropTypes.string 29 | } 30 | 31 | Button.defaultProps = { 32 | disabled: false, 33 | text: 'Button' 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Button/Button.stories.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import { withKnobs } from '@storybook/addon-knobs/react'; 4 | import { withSmartKnobs } from 'storybook-addon-smart-knobs'; 5 | import { Button } from './Button'; 6 | 7 | let stories = storiesOf('Buttons/Button', module); 8 | 9 | stories.addDecorator(withSmartKnobs).addDecorator(withKnobs); 10 | 11 | stories.add('Default', () => 12 |