diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..86c445f --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react"] +} diff --git a/.eslintrc b/.eslintrc index f392780..365c9a4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,9 +1,9 @@ { - "extends": "eslint-config-airbnb", - "env": { - "browser": true - }, + "extends": "airbnb", + parser: "babel-eslint", "rules": { + "react/prop-types": [2], + "no-unused-vars": 0, "react/no-multi-comp": 0, "import/default": 0, "import/no-duplicates": 0, @@ -19,16 +19,16 @@ "react/jsx-filename-extension": 0, "arrow-body-style": 0, "react/jsx-space-before-closing": 0, + "no-undef": 0, # Should look into this one # "eol-last": 0, "max-len": ["error", 120], "react/forbid-prop-types": 0, "object-curly-spacing": 0 }, "plugins": [ "react" ], - "settings": { - "import/parser": "babel-eslint", + "settings": { "import/resolve": { "moduleDirectory": ["node_modules", "src"] } } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index f430b96..fca988f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ .idea node_modules npm-debug.log +.webpack.config.js.swp +.eslintcache -# Dest assets folders -public/css -public/js +# Build folder +dist/ diff --git a/.stylelintrc b/.stylelintrc index 6ef1afa..cfd499c 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,19 +1,7 @@ { "rules": { - "block-no-empty": null, - "color-no-invalid-hex": true, - "comment-empty-line-before": [ "always", { - "ignore": ["stylelint-commands", "between-comments"], - } ], - "declaration-colon-space-after": "always", - "indentation": ["tab", { - "except": ["value"] - }], "max-empty-lines": 2, - "rule-nested-empty-line-before": [ "always", { - "except": ["first-nested"], - "ignore": ["after-comment"], - } ], - "unit-whitelist": ["em", "rem", "%", "s"] + "max-line-length": 90, + "max-nesting-depth": 3, } } diff --git a/.travis.yml b/.travis.yml index cb71dab..d0151b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,4 @@ before_install: npm install npm --version script: - - npm run js-lint -s - - npm run sass-lint -s + - npm run lint diff --git a/Gulpfile.js b/Gulpfile.js deleted file mode 100644 index 8fe57f5..0000000 --- a/Gulpfile.js +++ /dev/null @@ -1,106 +0,0 @@ -var gulp = require('gulp'); -var eslint = require('gulp-eslint'); -var uglify = require('gulp-uglify'); -var concat = require('gulp-concat'); -var cleanCSS = require('gulp-clean-css'); -var gulpStyleLint = require('gulp-stylelint'); -var browserSync = require('browser-sync').create(); -var sass = require('gulp-sass'); -var autoprefixer = require('gulp-autoprefixer'); -var notify = require('gulp-notify'); -var babelify = require('babelify'); -var browserify = require('browserify'); -var source = require('vinyl-source-stream'); -var buffer = require('vinyl-buffer'); -var sourcemaps = require('gulp-sourcemaps'); - - -// Static Server + watching scss/html files -gulp.task('serve', ['sass', 'js'], function() { - browserSync.init({ - server: './public' - }); - gulp.watch('app/**/*.scss', ['sass']); - gulp.watch('app/**/*.js', ['js']); - gulp.watch('public/*.html').on('change', browserSync.reload); -}); - -// Run lint for sass -gulp.task('stylelint', function lintCssTask() { - return gulp.src('./app/main.scss') - .pipe(gulpStyleLint({ - reporters: [ - {formatter: 'string', console: true} - ] - })); -}); - -// Run lint for js -gulp.task('jslint', function() { - return gulp.src(['app/**/*.js']) - .pipe(eslint()) - .pipe(eslint.format()); -}); - -// Min js files -gulp.task('uglify', ['js'], function() { - return gulp.src('./public/js/build.js') - .pipe(uglify()) - .pipe(concat('build.js')) - .pipe(gulp.dest('./public/js')) -}); - -// Min css files -gulp.task('minify-css', ['sass'], function() { - return gulp.src('./public/css/main.css') - .pipe(cleanCSS()) - .pipe(concat('main.css')) - .pipe(gulp.dest('./public/css')) -}); - -// Compile sass into CSS & auto-inject into browsers -gulp.task('sass', ['stylelint'], function() { - return gulp.src('./app/main.scss') - .pipe(sass().on('error', sass.logError)) - .pipe(autoprefixer({ - browsers: ['last 2 versions'], - cascade: false - })) - .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest('./public/css')) - .pipe(browserSync.stream()) - .pipe(notify({message: 'CSS created!', onLast: true})); -}); - -// Transpile ES6 js (React app) into JS & auto-inject into browsers -gulp.task('js', ['jslint'], function() { - var bundler = browserify('./app/app.js').transform("babelify", {presets: ["es2015", "react"]}); - return bundler.bundle() - .on('error', function(err) { console.error(err); this.emit('end'); }) - .pipe(source('build.js')) - .pipe(buffer()) - .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest('./public/js/')) - .pipe(browserSync.stream()) - .pipe(notify({message: 'JS bundle created!', onLast: true})); -}); - -// PRODUCTION -gulp.task('set-prod-node-env', function() { - return process.env.NODE_ENV = 'production'; -}); - -gulp.task('production', ['uglify', 'minify-css', 'set-prod-node-env']); - -// Start server without build -gulp.task('start', ['production'], function() { - browserSync.init({ - server: './public' - }); -}); - -// Tasks -gulp.task('default', ['serve']); - diff --git a/README.md b/README.md index 5f29b35..49ddad5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ This base project allows you to have a basic React App folder structure followin It contain some basic components that you can use. All the components are not (or minimally) stylized. The main objective of this project is to have a basic structure, that is the reason we are not doing complex styles and/or adding many libs. +Also, each component is a JSX file and a .scss file that is associated to the component. This means, all the +components for this project should be modular, to make it easy its usage between different apps. + ## Tools Using these package, you will be able to start a new fresh React project with the basic folder and file structures. @@ -17,17 +20,19 @@ web server in your machine that automatically updates the code and the styles wh ## Features -* ES6 to JS Transpile -* SASS to CSS -* React Router +* Webpack +* ES6 +* SASS +* React Router * Auto watcher for JS and SCSS files * Atomic design project structure -* Launch server that updates itself every time we change a file with browsersync +* Launch server with hot-reload using webpack * JS Lint ( extending airbnb eslint styles ) * Styleint ## TODO List * Tests +* Upgrade to Webpack 2.^ once stable ## Requirements * nodejs v5.* @@ -45,11 +50,6 @@ Clone this repo (Be sure you delete the .git file, or move the files to your own git clone -b master --single-branch --depth 1 git@github.com:Rulox/react-atomic-structure.git ``` -##### Install Gulp globally -```bash -npm install -g gulp -``` - ##### Install npm dependencies ```bash npm install @@ -59,13 +59,12 @@ npm install ```bash npm run start ``` -A browser window should open, if not, open it manually and go to http://localhost:3000/ (or any other URL+PORT that your terminal says). You can start working right now in the code, and all the changes will be visible in the browser just opened. +A browser webpack server should be ready on http://localhost:8080/ (or any other URL+PORT that your terminal says). You can start working right now in the code, and all the changes will be visible in the browser just opened. ## Predefined components But first, [What is Atomic Design?](http://bradfrost.com/blog/post/atomic-web-design/) -These components are just an idea on how to develop your application following the Atomic Design. Feel free to upgrade/delete -them in order to do your own app! +These components are just an idea on how to develop your application following the Atomic Design. Feel free to upgrade/delete them in order to do your own app! #### Atoms (stateless component) * Anchor @@ -94,9 +93,7 @@ them in order to do your own app! To create a new component, just create a new folder in the atoms/molecules/organisms/templates folder with the name of your component. -Create now the React component in the js file. Also create your .scss file and remember to import it in the _style.scss -of the parent folder (For example, if you're creating a new atom called Checkbox, you should have `atoms/Checkbox/_style.scss`. So in -the main style file for atoms `atoms/_style.scss` just import your new scss file so it can be imported. +Create now the React component in the jsx file. Also create your .scss file and remember to import it in the component jsx file using `require`. ## NPM Scripts This project comes with the following scripts to help you. @@ -105,33 +102,18 @@ This project comes with the following scripts to help you. npm run start ``` 1. Create CSS and JS bundles from Sass and JS. -2. Launch a browsersync web server and open default browser. +2. Launch a web server. 3. Launch watchers on JS/CSS files. ```bash -npm run build-dev -``` -1. Build CSS and JS from sources but does not start browsersync server. - -```bash -npm run build-prod +npm run build ``` -1. Build CSS and JS minified and ready for production but does not start browsersync server. +1. Build CSS and JS files. With this command, the js will be minified, the bundle version will be used in the html. ```bash -npm run start-server -``` -1. Run the server serving the `/public` folder using browsersync. - -```bash -npm run js-lint +npm run lint ``` 1. Launch JS Lint checker. -```bash -npm run sass-lint -``` -1. Launch SASS Lint checker. - ## Contributions Feel free to create a pull request or create an issue to add features or fix bugs. diff --git a/app/components/atoms/Base/_style.scss b/app/_style.scss similarity index 67% rename from app/components/atoms/Base/_style.scss rename to app/_style.scss index 6269caf..c600317 100644 --- a/app/components/atoms/Base/_style.scss +++ b/app/_style.scss @@ -1,4 +1,6 @@ +/* General Styles */ + html, body, ul, li { margin: 0; padding: 0; -} +} \ No newline at end of file diff --git a/app/app.js b/app/app.js deleted file mode 100644 index 8293034..0000000 --- a/app/app.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Router, Route, IndexRoute, browserHistory } from 'react-router'; - -import Home from './components/templates/Home/Home'; -import Main from './components/templates/Main/Main'; -import About from './components/templates/About/About'; - -const App = () => { - return ( - - - - - - - ); -}; - -ReactDOM.render(, document.getElementById('app')); diff --git a/app/app.jsx b/app/app.jsx new file mode 100644 index 0000000..5441918 --- /dev/null +++ b/app/app.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Router, Route, IndexRoute, browserHistory } from 'react-router'; + +// Components +import Home from './components/templates/Home/Home'; +import Main from './components/templates/Main/Main'; +import About from './components/templates/About/About'; + +require('./_style.scss'); + +const App = () => ( + + + + + + +); + +if (typeof window !== 'undefined') { + ReactDOM.render(, document.getElementById('app')); +} diff --git a/app/components/atoms/Anchor/Anchor.js b/app/components/atoms/Anchor/Anchor.js deleted file mode 100644 index c400935..0000000 --- a/app/components/atoms/Anchor/Anchor.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -const Anchor = (props) => { - return ( - {props.text} - ); -}; - -Anchor.propTypes = { - text: React.PropTypes.string.isRequired -}; - -export default Anchor; diff --git a/app/components/atoms/Anchor/Anchor.jsx b/app/components/atoms/Anchor/Anchor.jsx new file mode 100644 index 0000000..6f5d9d1 --- /dev/null +++ b/app/components/atoms/Anchor/Anchor.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +require('./_style.scss'); + +const Anchor = props => ( + {props.text} +); + +Anchor.propTypes = { + text: React.PropTypes.string.isRequired, +}; + +export default Anchor; diff --git a/app/components/atoms/Button/Button.js b/app/components/atoms/Button/Button.js deleted file mode 100644 index a03c062..0000000 --- a/app/components/atoms/Button/Button.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -const Button = (props) => { - return ( - - ); -}; - -Button.propTypes = { - children: React.PropTypes.string.isRequired -}; - -export default Button; diff --git a/app/components/atoms/Button/Button.jsx b/app/components/atoms/Button/Button.jsx new file mode 100644 index 0000000..34a9541 --- /dev/null +++ b/app/components/atoms/Button/Button.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +require('./_style.scss'); + +const Button = props => ( + +); + +Button.propTypes = { + text: React.PropTypes.string.isRequired, +}; + +export default Button; diff --git a/app/components/atoms/Image/Image.js b/app/components/atoms/Image/Image.js deleted file mode 100644 index 555aa37..0000000 --- a/app/components/atoms/Image/Image.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -const Image = (props) => { - return ( - {props.alt}/ - ); -}; - -Image.propTypes = { - src: React.PropTypes.string.isRequired, - alt: React.PropTypes.string -}; - -export default Image; \ No newline at end of file diff --git a/app/components/atoms/Image/Image.jsx b/app/components/atoms/Image/Image.jsx new file mode 100644 index 0000000..be9c17b --- /dev/null +++ b/app/components/atoms/Image/Image.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +require('./_style.scss'); + +const Image = props => ( + {props.alt} +); + +Image.propTypes = { + src: React.PropTypes.string.isRequired, + alt: React.PropTypes.string, +}; + +export default Image; diff --git a/app/components/atoms/Input/Input.js b/app/components/atoms/Input/Input.js deleted file mode 100644 index a42035c..0000000 --- a/app/components/atoms/Input/Input.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -const Input = (props) => { - return ( - - ); -}; - -Input.propTypes = { - text: React.PropTypes.string, - type: React.PropTypes.string, - placeholder: React.PropTypes.string -}; - -export default Input; \ No newline at end of file diff --git a/app/components/atoms/Input/Input.jsx b/app/components/atoms/Input/Input.jsx new file mode 100644 index 0000000..ce6c993 --- /dev/null +++ b/app/components/atoms/Input/Input.jsx @@ -0,0 +1,15 @@ +import React from 'react'; + +require('./_style.scss'); + +const Input = props => ( + +); + +Input.propTypes = { + text: React.PropTypes.string, + type: React.PropTypes.string, + placeholder: React.PropTypes.string, +}; + +export default Input; diff --git a/app/components/atoms/Label/Label.js b/app/components/atoms/Label/Label.js deleted file mode 100644 index d71992b..0000000 --- a/app/components/atoms/Label/Label.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -const Label = (props) => { - return ( - {props.text} - ); -}; - -Label.propTypes = { - text: React.PropTypes.string.isRequired -}; - -export default Label; \ No newline at end of file diff --git a/app/components/atoms/Label/Label.jsx b/app/components/atoms/Label/Label.jsx new file mode 100644 index 0000000..352769c --- /dev/null +++ b/app/components/atoms/Label/Label.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +require('./_style.scss'); + +const Label = props => ( + {props.text} +); + +Label.propTypes = { + text: React.PropTypes.string.isRequired, +}; + +export default Label; diff --git a/app/components/atoms/Paragraph/Paragraph.js b/app/components/atoms/Paragraph/Paragraph.js deleted file mode 100644 index 08fec3b..0000000 --- a/app/components/atoms/Paragraph/Paragraph.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -const Paragraph = (props) => { - return ( -

{props.children}

- ); -}; - -Paragraph.propTypes = { - children: React.PropTypes.string.isRequired -}; - -export default Paragraph; diff --git a/app/components/atoms/Paragraph/Paragraph.jsx b/app/components/atoms/Paragraph/Paragraph.jsx new file mode 100644 index 0000000..fea79c6 --- /dev/null +++ b/app/components/atoms/Paragraph/Paragraph.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +require('./_style.scss'); + +const Paragraph = props => ( +

{props.text}

+); + +Paragraph.propTypes = { + text: React.PropTypes.string.isRequired, +}; + +export default Paragraph; diff --git a/app/components/atoms/Title/Title.js b/app/components/atoms/Title/Title.js deleted file mode 100644 index 0ee331c..0000000 --- a/app/components/atoms/Title/Title.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -const Title = (props) => { - return ( -

{props.children}

- ); -}; - -Title.propTypes = { - children: React.PropTypes.string.isRequired -}; - -export default Title; diff --git a/app/components/atoms/Title/Title.jsx b/app/components/atoms/Title/Title.jsx new file mode 100644 index 0000000..d05e993 --- /dev/null +++ b/app/components/atoms/Title/Title.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +require('./_style.scss'); + +const Title = props => ( +

{props.text}

+); + +Title.propTypes = { + text: React.PropTypes.string.isRequired, +}; + +export default Title; diff --git a/app/components/atoms/Title/_style.scss b/app/components/atoms/Title/_style.scss index 6737fc2..79204e6 100644 --- a/app/components/atoms/Title/_style.scss +++ b/app/components/atoms/Title/_style.scss @@ -1,2 +1,3 @@ .a__title { + color: #333333; } \ No newline at end of file diff --git a/app/components/atoms/_style.scss b/app/components/atoms/_style.scss deleted file mode 100644 index deddbb8..0000000 --- a/app/components/atoms/_style.scss +++ /dev/null @@ -1,9 +0,0 @@ -/* Atoms styles */ -@import 'Anchor/style'; -@import 'Label/style'; -@import 'Button/style'; -@import 'Input/style'; -@import 'Image/style'; -@import 'Title/style'; -@import 'Paragraph/style'; -@import 'Base/style'; diff --git a/app/components/molecules/Content/Content.js b/app/components/molecules/Content/Content.jsx similarity index 51% rename from app/components/molecules/Content/Content.js rename to app/components/molecules/Content/Content.jsx index bd97b91..da70a03 100644 --- a/app/components/molecules/Content/Content.js +++ b/app/components/molecules/Content/Content.jsx @@ -2,18 +2,18 @@ import React from 'react'; import Title from '../../atoms/Title/Title'; import Paragraph from '../../atoms/Paragraph/Paragraph'; -const Content = (props) => { - return ( -
- {props.title} - {props.text} -
- ); -}; +require('./_style.scss'); + +const Content = props => ( +
+ + <Paragraph text={props.text} /> + </div> +); Content.propTypes = { title: React.PropTypes.string, - text: React.PropTypes.string + text: React.PropTypes.string, }; export default Content; diff --git a/app/components/molecules/LabeledInput/LabeledInput.js b/app/components/molecules/LabeledInput/LabeledInput.js deleted file mode 100644 index d7dacbc..0000000 --- a/app/components/molecules/LabeledInput/LabeledInput.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import Label from '../../atoms/Label/Label'; -import Input from '../../atoms/Input/Input'; - -const LabeledInput = (props) => { - return ( - <div className="m__labeled_input"> - <Label text={props.label}/> - <Input value="" placeholder={props.placeholder} type="text"/> - </div> - ); -}; - -LabeledInput.propTypes = { - label: React.PropTypes.string.isRequired, - placeholder: React.PropTypes.string -}; - -export default LabeledInput; \ No newline at end of file diff --git a/app/components/molecules/LabeledInput/LabeledInput.jsx b/app/components/molecules/LabeledInput/LabeledInput.jsx new file mode 100644 index 0000000..c603b74 --- /dev/null +++ b/app/components/molecules/LabeledInput/LabeledInput.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Label from '../../atoms/Label/Label'; +import Input from '../../atoms/Input/Input'; + +require('./_style.scss'); + +const LabeledInput = props => ( + <div className="m__labeled_input"> + <Label text={props.label} /> + <Input value="" placeholder={props.placeholder} type="text" /> + </div> +); + +LabeledInput.propTypes = { + label: React.PropTypes.string.isRequired, + placeholder: React.PropTypes.string, +}; + +export default LabeledInput; diff --git a/app/components/molecules/_style.scss b/app/components/molecules/_style.scss deleted file mode 100644 index 3867148..0000000 --- a/app/components/molecules/_style.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'LabeledInput/style'; -@import 'Content/style'; \ No newline at end of file diff --git a/app/components/organisms/Article/Article.js b/app/components/organisms/Article/Article.js deleted file mode 100644 index 6a52a3f..0000000 --- a/app/components/organisms/Article/Article.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import Content from '../../molecules/Content/Content'; - -const Article = (props) => { - return ( - <div className="o__article"> - <img className="article_featured" src={props.image.src} alt={props.image.alt}/> - <Content text={props.content} title={props.title}/> - </div> - ); -}; - -Article.propTypes = { - image: React.PropTypes.object, - title: React.PropTypes.string.isRequired, - content: React.PropTypes.string -}; - -export default Article; \ No newline at end of file diff --git a/app/components/organisms/Article/Article.jsx b/app/components/organisms/Article/Article.jsx new file mode 100644 index 0000000..71d6409 --- /dev/null +++ b/app/components/organisms/Article/Article.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Content from '../../molecules/Content/Content'; + +require('./_style.scss'); + +const Article = props => ( + <div className="o__article"> + <img className="article_featured" src={props.image.src} alt={props.image.alt} /> + <Content text={props.content} title={props.title} /> + </div> +); + +Article.propTypes = { + image: React.PropTypes.object, + title: React.PropTypes.string.isRequired, + content: React.PropTypes.string, +}; + +export default Article; diff --git a/app/components/organisms/Form/Form.js b/app/components/organisms/Form/Form.js deleted file mode 100644 index ccf78d0..0000000 --- a/app/components/organisms/Form/Form.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import LabeledInput from '../../molecules/LabeledInput/LabeledInput'; -import Button from '../../atoms/Button/Button'; - -const Form = (props) => { - return ( - <form className="o__form"> - { - props.fields.map((field, i) => { - return ( - <LabeledInput label={field.label} placeholder={field.placeholder} key={i}/> - ); - }) - } - <Button>{props.buttonText}</Button> - </form> - ); -}; - -Form.propTypes = { - fields: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - buttonText: React.PropTypes.string.isRequired -}; - -export default Form; diff --git a/app/components/organisms/Form/Form.jsx b/app/components/organisms/Form/Form.jsx new file mode 100644 index 0000000..5e36aec --- /dev/null +++ b/app/components/organisms/Form/Form.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import LabeledInput from '../../molecules/LabeledInput/LabeledInput'; +import Button from '../../atoms/Button/Button'; + +require('./_style.scss'); + +const Form = props => ( + <form className="o__form"> + { + props.fields.map((field, i) => (<LabeledInput label={field.label} placeholder={field.placeholder} key={i}/>)) + } + <Button text={props.buttonText} /> + </form> +); + +Form.propTypes = { + fields: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, + buttonText: React.PropTypes.string.isRequired, +}; + +export default Form; diff --git a/app/components/organisms/Nav/Nav.js b/app/components/organisms/Nav/Nav.js deleted file mode 100644 index 5115cda..0000000 --- a/app/components/organisms/Nav/Nav.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router'; - -const Nav = () => { - return ( - <nav className="o__nav"> - <ul> - <li><Link to="/">Home</Link></li> - <li><Link to="/about">About</Link></li> - <li><Link to="/another-page">Another Page</Link></li> - </ul> - </nav> - ); -}; - -export default Nav; \ No newline at end of file diff --git a/app/components/organisms/Nav/Nav.jsx b/app/components/organisms/Nav/Nav.jsx new file mode 100644 index 0000000..bfaccee --- /dev/null +++ b/app/components/organisms/Nav/Nav.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Link } from 'react-router'; + +require('./_style.scss'); + +const Nav = () => +( + <nav className="o__nav"> + <ul> + <li><Link to="/">Home</Link></li> + <li><Link to="/about">About</Link></li> + <li><Link to="/another-page">Another Page</Link></li> + </ul> + </nav> +); + +export default Nav; diff --git a/app/components/organisms/_style.scss b/app/components/organisms/_style.scss deleted file mode 100644 index 0ca02b0..0000000 --- a/app/components/organisms/_style.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'Article/style'; -@import 'Form/style'; -@import 'Nav/style' diff --git a/app/components/templates/About/About.js b/app/components/templates/About/About.js deleted file mode 100644 index d6acc22..0000000 --- a/app/components/templates/About/About.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import Title from '../../atoms/Title/Title'; - -const About = () => { - return ( - <Title>"This is an example About page" - ); -}; - -export default About; diff --git a/app/components/templates/About/About.jsx b/app/components/templates/About/About.jsx new file mode 100644 index 0000000..4d1c416 --- /dev/null +++ b/app/components/templates/About/About.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import Title from '../../atoms/Title/Title'; + +require('./_style.scss'); + +const About = () => (); + +export default About; diff --git a/app/components/templates/Home/Home.js b/app/components/templates/Home/Home.js deleted file mode 100644 index 3ec66f8..0000000 --- a/app/components/templates/Home/Home.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import Title from '../../atoms/Title/Title'; -import Article from '../../organisms/Article/Article'; -import Form from '../../organisms/Form/Form'; - -const Home = (props) => { - return ( - <div> - <Title>Basic React App - Example of an atomic Blog -
- -
- -
- - Example of an Atomic Form -
-
- ); -}; - -Home.propTypes = { - form: React.PropTypes.array -}; - -Home.defaultProps = { - form: [ - { - label: 'Input 1', - placeholder: 'Placeholder for Input 1' - }, - { - label: 'Input 2', - placeholder: 'Placeholder for Input 2' - } - ] -}; - -export default Home; diff --git a/app/components/templates/Home/Home.jsx b/app/components/templates/Home/Home.jsx new file mode 100644 index 0000000..a82cfbc --- /dev/null +++ b/app/components/templates/Home/Home.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import Title from '../../atoms/Title/Title'; +import Article from '../../organisms/Article/Article'; +import Form from '../../organisms/Form/Form'; + +require('./_style.scss'); + +const Home = props => ( +
+ + <Title text="Example of an atomic Blog" /> + <Article + title="This is an article" + image={{ src: 'http://placehold.it/300x200', alt: 'Placehold' }} + content="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc id nulla cursus, + hendrerit lectus id, pulvinar massa." + /> + + <Article + title="This is an article" + image={{ src: 'http://placehold.it/300x200', alt: 'Placehold' }} + content="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc id nulla cursus, + hendrerit lectus id, pulvinar massa." + /> + + <hr /> + <Title text="Example of an Atomic Form" /> + <Form fields={props.form} buttonText="Submit" /> + </div> +); + +Home.propTypes = { + form: React.PropTypes.array, +}; + +Home.defaultProps = { + form: [ + { + label: 'Input 1', + placeholder: 'Placeholder for Input 1', + }, + { + label: 'Input 2', + placeholder: 'Placeholder for Input 2', + }, + ], +}; + +export default Home; diff --git a/app/components/templates/Main/Main.js b/app/components/templates/Main/Main.js deleted file mode 100644 index 715b0a5..0000000 --- a/app/components/templates/Main/Main.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -import Nav from '../../organisms/Nav/Nav'; - -const Main = (props) => { - return ( - <div className="app"> - <Nav /> - - {props.children} - </div> - ); -}; - -Main.propTypes = { - children: React.PropTypes.node -}; - -export default Main; \ No newline at end of file diff --git a/app/components/templates/Main/Main.jsx b/app/components/templates/Main/Main.jsx new file mode 100644 index 0000000..1539a06 --- /dev/null +++ b/app/components/templates/Main/Main.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import Nav from '../../organisms/Nav/Nav'; + +const Main = props => ( + <div className="app"> + <Nav /> + {props.children} + </div> +); + +Main.propTypes = { + children: React.PropTypes.node, +}; + +export default Main; diff --git a/app/components/templates/Main/_style.scss b/app/components/templates/Main/_style.scss deleted file mode 100644 index e69de29..0000000 diff --git a/app/components/templates/_style.scss b/app/components/templates/_style.scss deleted file mode 100644 index 04286bf..0000000 --- a/app/components/templates/_style.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'Home/style'; -@import 'Main/style'; -@import 'About/style'; diff --git a/app/main.scss b/app/main.scss deleted file mode 100644 index 8771e5a..0000000 --- a/app/main.scss +++ /dev/null @@ -1,9 +0,0 @@ -// Utils, mixins, variables, etc. -// Atoms -@import 'components/atoms/style'; -// Molecules -@import 'components/molecules/style'; -// Organisms -@import 'components/organisms/style'; -// Templates -@import 'components/templates/style'; diff --git a/package.json b/package.json index 3939a59..d47177c 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,11 @@ "version": "1.0.0", "description": "Basic Structure for React app following Atomic Design", "scripts": { - "start": "gulp", - "start-server": "gulp start", - "build-dev": "gulp js && gulp sass", - "build-prod": "gulp production", - "js-lint": "gulp jslint", - "sass-lint": "gulp stylelint" + "start": "webpack-dev-server --inline --hot --progress --colors", + "build": "webpack --config ./webpack-production.config.js --progress --colors", + "lint": "eslint ./app/ --ext .js --ext .jsx --fix", + "watch": "webpack --watch --progress", + "clean": "rm -rf dist && npm cache clean" }, "repository": { "type": "git", @@ -27,32 +26,33 @@ }, "homepage": "https://github.com/Rulox/react-atomic-structure#readme", "dependencies": { - "gulp": "^3.9.1", "react": "^15.3.1", "react-dom": "^15.3.1", - "react-router": "^2.8.1" + "react-router": "^2.8.1", + "extract-text-webpack-plugin": "^1.0.1", + "webpack-uglify-js-plugin": "^1.1.9", + "stylelint-webpack-plugin": "^0.4.0", + "html-webpack-plugin": "^2.24.1" }, "devDependencies": { + "babel-core": "^6.18.2", + "babel-eslint": "^7.1.0", + "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.14.0", - "babel-preset-react": "^6.11.1", + "babel-preset-react": "^6.16.0", "babel-preset-stage-0": "^6.5.0", - "babelify": "^7.3.0", - "browser-sync": "^2.14.0", - "browserify": "^13.1.0", - "eslint-config-airbnb": "^11.1.0", - "eslint-plugin-import": "^1.15.0", - "eslint-plugin-jsx-a11y": "^2.2.2", - "eslint-plugin-react": "^6.3.0", - "gulp-autoprefixer": "^3.1.1", - "gulp-clean-css": "^2.0.12", - "gulp-concat": "^2.6.0", - "gulp-eslint": "^3.0.1", - "gulp-notify": "^2.2.0", - "gulp-sass": "^2.3.2", - "gulp-sourcemaps": "^1.6.0", - "gulp-stylelint": "^3.2.0", - "gulp-uglify": "^2.0.0", - "vinyl-buffer": "^1.0.0", - "vinyl-source-stream": "^1.1.0" + "css-loader": "^0.25.0", + "eslint": "^3.9.1", + "eslint-config-airbnb": "^13.0.0", + "eslint-loader": "^1.6.1", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^2.2.3", + "eslint-plugin-react": "^6.6.0", + "node-sass": "^3.11.2", + "react-hot-loader": "^3.0.0-beta.6", + "sass-loader": "^4.0.2", + "style-loader": "^0.13.1", + "webpack": "^1.13.3", + "webpack-dev-server": "^1.16.2" } } diff --git a/public/index.html b/public/index.html index 1709f72..006af23 100644 --- a/public/index.html +++ b/public/index.html @@ -3,10 +3,8 @@ <head> <meta charset="UTF-8"> <title>My App -
- - \ No newline at end of file + diff --git a/webpack-production.config.js b/webpack-production.config.js new file mode 100644 index 0000000..0224ecb --- /dev/null +++ b/webpack-production.config.js @@ -0,0 +1,11 @@ +const webpack = require("webpack"); +const config = require('./webpack.config'); + +const UglifyJsPluginConfig = new webpack.optimize.UglifyJsPlugin({ + minimize: true +}); + +config.output.filename = 'bundle.min.js'; +config.plugins.push(UglifyJsPluginConfig); + +module.exports = config; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..a21026a --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,64 @@ +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const StyleLintPlugin = require('stylelint-webpack-plugin'); + +const StyleLintPluginConfig = new StyleLintPlugin({ + configFile: '.stylelintrc', + files: [ + 'app/*.s?(a|c)ss', + 'app/components/*/*.s?(a|c)ss', + 'app/components/*/*/*.s?(a|c)ss' + ], + failOnError: false, + quiet: false, +}); +const ExtractTextPluginConfig = new ExtractTextPlugin( + 'style/main.css', { + allChunks: true, + } +); +const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({ + template: `${__dirname}/public/index.html`, + filename: 'index.html', + inject: 'body', +}); + +module.exports = { + entry: './app/app.jsx', + + output: { + filename: 'index.js', + path: `${__dirname}/dist`, + publicPath: '/', + }, + + devtool: 'source-map', + + module: { + preLoaders: [ + { test: /\.jsx?$/, exclude: /node_modules/, loader: 'eslint-loader' } + ], + loaders: [ + /** *** Needed for webpack 2.^ *** **/ + // { enforce: 'pre', test: /\.jsx?$/, exclude: /node_modules/, loader: 'eslint-loader', } + { test: /\.jsx?$/, exclude: /(node_modules)/, loader: 'react-hot-loader/webpack!babel' }, + { test: /\.jsx?$/, exclude: /(node_modules)/, loader: 'babel' }, + { test: /\.scss$/, loader: ExtractTextPluginConfig.extract('css!sass') }, + ], + }, + + eslint: { + configFile: './.eslintrc', + failOnError: true + }, + + resolve: { + extensions: ['', '.js', '.jsx'], + }, + + plugins: [ + ExtractTextPluginConfig, + HTMLWebpackPluginConfig, + StyleLintPluginConfig + ], +};