├── .gitignore ├── test ├── .eslintrc ├── index.js └── src │ └── components │ └── app │ └── index.js ├── docs └── screenshot.png ├── src ├── index.js ├── theme-bootstrap │ ├── form │ │ ├── styles.less │ │ └── index.js │ ├── app │ │ ├── styles.less │ │ └── index.js │ ├── index.js │ ├── link │ │ └── styles.less │ ├── notification │ │ ├── index.js │ │ └── styles.less │ ├── heading │ │ ├── index.js │ │ └── styles.less │ ├── input │ │ └── styles.less │ └── button │ │ └── styles.less ├── assets │ └── index.html └── components │ ├── app │ ├── styles.less │ └── index.js │ └── authentication │ ├── styles.less │ └── index.js ├── .eslintrc ├── conf ├── karma.build.js ├── karma.dev.js ├── webpack.dev.js ├── karma.common.js ├── webpack.build.js ├── webpack.common.js └── webpack.test.js ├── .babelrc ├── .editorconfig ├── .travis.yml ├── license.md ├── tasks.js ├── readme.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | coverage/ 4 | *.log 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "rebem/configs/test" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rebem/starter-kit/HEAD/docs/screenshot.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { render } from 'react-dom'; 2 | import App from '#app'; 3 | 4 | render(App(), document.getElementById('app')); 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "rebem/configs/common", 4 | "rebem/configs/babel", 5 | "rebem/configs/react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /conf/karma.build.js: -------------------------------------------------------------------------------- 1 | import karmaCommonConfig from './karma.common'; 2 | 3 | export default { 4 | ...karmaCommonConfig, 5 | singleRun: true, 6 | autoWatch: false 7 | }; 8 | -------------------------------------------------------------------------------- /src/theme-bootstrap/form/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | @import '~bootstrap/less/mixins/forms'; 3 | @import '~bootstrap/less/mixins/vendor-prefixes'; 4 | 5 | .form { 6 | } 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "stage-1" ], 3 | "plugins": [ "transform-runtime" ], 4 | "env": { 5 | "development": { 6 | "presets": [ "react-hmre" ] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/theme-bootstrap/app/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | 3 | body { 4 | font-family: @font-family-base; 5 | font-size: @font-size-base; 6 | line-height: @line-height-base; 7 | } 8 | -------------------------------------------------------------------------------- /src/theme-bootstrap/app/index.js: -------------------------------------------------------------------------------- 1 | import { BEM } from 'rebem'; 2 | 3 | export default function App({ children, ...props }) { 4 | return BEM({ 5 | ...props, 6 | block: 'app' 7 | }, children); 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | App 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/theme-bootstrap/form/index.js: -------------------------------------------------------------------------------- 1 | import { BEM } from 'rebem'; 2 | 3 | export default function({ children, ...props }) { 4 | return BEM({ 5 | ...props, 6 | block: 'form', 7 | tag: 'form' 8 | }, children); 9 | } 10 | -------------------------------------------------------------------------------- /src/theme-bootstrap/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | module.exports = { 4 | path: path.resolve(__dirname, './'), 5 | files: { 6 | main: 'index.js', 7 | styles: 'styles.less' 8 | }, 9 | importFactory: true 10 | }; 11 | -------------------------------------------------------------------------------- /conf/karma.dev.js: -------------------------------------------------------------------------------- 1 | import karmaCommonConfig from './karma.common'; 2 | 3 | export default { 4 | ...karmaCommonConfig, 5 | singleRun: false, 6 | autoWatch: true, 7 | reporters: [ 8 | 'clear-screen', 9 | ...karmaCommonConfig.reporters 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /src/theme-bootstrap/link/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | 3 | @link-color: @brand-primary; 4 | @link-hover-color: darken(@link-color, 15%); 5 | 6 | .link { 7 | color: @link-color; 8 | 9 | &:hover { 10 | color: @link-hover-color; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [*.{json,yml}] 13 | indent_size = 2 14 | 15 | [{.babelrc,.eslintrc}] 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://docs.travis-ci.com/user/customizing-the-build/ 2 | 3 | sudo: false 4 | 5 | git: 6 | depth: 1 7 | 8 | language: node_js 9 | 10 | node_js: 11 | # - "0.12" 12 | - "4" 13 | - "5" 14 | 15 | branches: 16 | only: 17 | - master 18 | 19 | matrix: 20 | fast_finish: true 21 | 22 | before_install: 23 | - npm install -g npm 24 | - npm --version 25 | 26 | script: npm start test 27 | -------------------------------------------------------------------------------- /src/components/app/styles.less: -------------------------------------------------------------------------------- 1 | .app { 2 | display: flex; 3 | min-height: 100vh; 4 | flex-direction: column; 5 | 6 | &__header { 7 | padding: 20px; 8 | color: #fff; 9 | background: #337ab7; 10 | } 11 | 12 | &__content { 13 | padding: 40px 20px; 14 | flex: 1; 15 | } 16 | 17 | &__footer { 18 | background: #ddd; 19 | padding: 20px; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/authentication/styles.less: -------------------------------------------------------------------------------- 1 | .authentication { 2 | position: relative; 3 | padding: 25px 0 15px; 4 | margin: 0 -15px 15px; 5 | border-radius: 4px; 6 | border: 1px solid #ddd; 7 | width: 400px; 8 | margin: auto; 9 | 10 | &__heading { 11 | margin: 0 20px 20px; 12 | } 13 | 14 | &__status { 15 | margin: 20px; 16 | 17 | &_empty { 18 | display: none; 19 | } 20 | } 21 | 22 | &__form-row { 23 | margin-top: 15px; 24 | padding: 0 15px; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/theme-bootstrap/notification/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | import { BEM } from 'rebem'; 3 | 4 | export default function Notification({ children, ...props }) { 5 | return BEM({ 6 | ...props, 7 | block: 'notification', 8 | mods: { 9 | ...props.mods, 10 | type: props.type || false 11 | } 12 | }, children); 13 | } 14 | 15 | Notification.propTypes = { 16 | type: PropTypes.oneOf([ 17 | 'success', 18 | 'info', 19 | 'warning', 20 | 'error' 21 | ]) 22 | }; 23 | -------------------------------------------------------------------------------- /src/theme-bootstrap/heading/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | import { BEM } from 'rebem'; 3 | 4 | export default function Heading({ children, type, ...props }) { 5 | return BEM({ 6 | ...props, 7 | tag: type, 8 | block: 'heading', 9 | mods: { 10 | ...props.mods, 11 | type 12 | } 13 | }, children); 14 | } 15 | 16 | Heading.defaultProps = { 17 | type: 'h1' 18 | }; 19 | 20 | Heading.propTypes = { 21 | type: PropTypes.oneOf([ 22 | 'h1', 23 | 'h2', 24 | 'h3', 25 | 'h4', 26 | 'h5', 27 | 'h6' 28 | ]) 29 | }; 30 | -------------------------------------------------------------------------------- /src/theme-bootstrap/notification/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | 3 | .notification { 4 | padding: 20px; 5 | border: 1px solid #eee; 6 | border-left-width: 5px; 7 | border-left-color: @brand-primary; 8 | border-radius: 3px; 9 | 10 | &_type { 11 | &_success { 12 | border-left-color: @brand-success; 13 | } 14 | 15 | &_info { 16 | border-left-color: @brand-info; 17 | } 18 | 19 | &_error { 20 | border-left-color: @brand-danger; 21 | } 22 | 23 | &_warning { 24 | border-left-color: @brand-warning; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/theme-bootstrap/heading/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | 3 | .heading { 4 | line-height: @headings-line-height; 5 | font-weight: normal; 6 | 7 | &_type { 8 | &_h1 { 9 | font-size: @font-size-h1; 10 | } 11 | 12 | &_h2 { 13 | font-size: @font-size-h2; 14 | } 15 | 16 | &_h3 { 17 | font-size: @font-size-h3; 18 | } 19 | 20 | &_h4 { 21 | font-size: @font-size-h4; 22 | } 23 | 24 | &_h5 { 25 | font-size: @font-size-h5; 26 | } 27 | 28 | &_h6 { 29 | font-size: @font-size-h6; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/theme-bootstrap/input/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | @import '~bootstrap/less/mixins/forms'; 3 | @import '~bootstrap/less/mixins/vendor-prefixes'; 4 | 5 | .input { 6 | display: block; 7 | width: 100%; 8 | 9 | &__control { 10 | height: @input-height-base; 11 | padding: @padding-base-vertical @padding-base-horizontal; 12 | font-size: @font-size-base; 13 | line-height: @line-height-base; 14 | color: @input-color; 15 | background-color: @input-bg; 16 | border: 1px solid @input-border; 17 | border-radius: @input-border-radius; 18 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); 19 | .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); 20 | .placeholder(); 21 | .form-control-focus(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/app/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import { blockFactory } from 'rebem'; 3 | 4 | import App from '#app'; 5 | import Authentication from '#authentication'; 6 | import Heading from '#heading'; 7 | import Link from '#link'; 8 | 9 | const block = 'app'; 10 | const Block = blockFactory(block); 11 | 12 | export default class extends Component { 13 | render() { 14 | return App(this.props, 15 | Block({ elem: 'header' }, 16 | Heading({ 17 | mix: { block, elem: 'title' } 18 | }, 'reBEM Starter Kit') 19 | ), 20 | Block({ elem: 'content' }, 21 | Authentication() 22 | ), 23 | Block({ elem: 'footer' }, 24 | 'Made with ', 25 | Link({ href: 'http://rebem.js.org/' }, 'reBEM') 26 | ) 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import chaiBEM from 'chai-bem'; 3 | import chaiSpies from 'chai-spies'; 4 | import chaiEnzyme from 'chai-enzyme'; 5 | 6 | import { ShallowWrapper, ReactWrapper } from 'enzyme'; 7 | 8 | chai.use(chaiBEM({ 9 | entityHook(entity) { 10 | if (entity instanceof ShallowWrapper || entity instanceof ReactWrapper) { 11 | return entity.prop('className'); 12 | } 13 | 14 | return entity; 15 | } 16 | })); 17 | chai.use(chaiSpies); 18 | chai.use(chaiEnzyme(({ wrapper }) => wrapper.debug())); 19 | 20 | // https://github.com/webpack/karma-webpack#alternative-usage 21 | 22 | // components 23 | const componentsTests = require.context('./src/components/', true, /\.js$/); 24 | const componentsSources = require.context('../src/components/', true, /\.js$/); 25 | 26 | componentsTests.keys().forEach(componentsTests); 27 | componentsSources.keys().forEach(componentsSources); 28 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | * Copyright (c) 2015–present Kir Belevich 4 | * Copyright (c) 2015–present Denis Koltsov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/theme-bootstrap/button/styles.less: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/less/variables'; 2 | @import '~bootstrap/less/pagination'; 3 | @import '~bootstrap/less/mixins/border-radius'; 4 | @import '~bootstrap/less/mixins/buttons'; 5 | @import '~bootstrap/less/mixins/pagination'; 6 | @import '~bootstrap/less/mixins/tab-focus'; 7 | @import '~bootstrap/less/mixins/vendor-prefixes'; 8 | 9 | .button { 10 | &__control { 11 | margin-bottom: 0; 12 | font-weight: @btn-font-weight; 13 | text-align: center; 14 | vertical-align: middle; 15 | border: 1px solid transparent; 16 | .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base); 17 | 18 | &, 19 | &:active, 20 | &.active { 21 | &:focus, 22 | &.focus { 23 | .tab-focus(); 24 | } 25 | } 26 | 27 | &:hover, 28 | &:focus, 29 | &.focus { 30 | color: @btn-default-color; 31 | text-decoration: none; 32 | } 33 | 34 | &:active, 35 | &.active { 36 | outline: 0; 37 | background-image: none; 38 | .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 39 | } 40 | .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /conf/webpack.dev.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | 4 | import webpackCommonConfig from './webpack.common'; 5 | 6 | export default { 7 | ...webpackCommonConfig, 8 | entry: [ 9 | 'webpack/hot/dev-server', 10 | './src/index' 11 | ], 12 | output: { 13 | ...webpackCommonConfig.output, 14 | path: path.resolve('./'), 15 | publicPath: '/', 16 | filename: 'bundle.js' 17 | }, 18 | // devtool: '#cheap-module-eval-source-map', 19 | module: { 20 | preLoaders: webpackCommonConfig.module.preLoaders, 21 | loaders: [ 22 | ...webpackCommonConfig.module.loaders, 23 | { 24 | test: /\.css$/, 25 | loaders: [ 26 | 'style', 27 | 'css?-minimize', 28 | 'postcss' 29 | ] 30 | }, 31 | { 32 | test: /\.less$/, 33 | loaders: [ 34 | 'style', 35 | 'css?-minimize', 36 | 'postcss', 37 | 'less' 38 | ] 39 | } 40 | ] 41 | }, 42 | plugins: [ 43 | ...webpackCommonConfig.plugins, 44 | new webpack.HotModuleReplacementPlugin(), 45 | new webpack.NoErrorsPlugin() 46 | ] 47 | }; 48 | -------------------------------------------------------------------------------- /tasks.js: -------------------------------------------------------------------------------- 1 | import Start from 'start'; 2 | import reporter from 'start-pretty-reporter'; 3 | import env from 'start-env'; 4 | import files from 'start-files'; 5 | import clean from 'start-clean'; 6 | import eslint from 'start-eslint'; 7 | import * as webpack from 'start-webpack'; 8 | import karma from 'start-karma'; 9 | 10 | const start = Start(reporter()); 11 | 12 | export function build() { 13 | return start( 14 | env('production'), 15 | files('build/'), 16 | clean(), 17 | webpack.build(require('./conf/webpack.build').default) 18 | ); 19 | } 20 | 21 | export function dev() { 22 | return start( 23 | env('development'), 24 | webpack.dev(require('./conf/webpack.dev').default) 25 | ); 26 | } 27 | 28 | export function lint() { 29 | return start( 30 | env('test'), 31 | files([ 'src/**/*.js', 'test/**/*.js', 'conf/**/*.js' ]), 32 | eslint() 33 | ); 34 | } 35 | 36 | export function test() { 37 | return start( 38 | env('test'), 39 | lint, 40 | files('coverage/'), 41 | clean(), 42 | karma(require('./conf/karma.build').default) 43 | ); 44 | } 45 | 46 | export function tdd() { 47 | return start( 48 | env('test'), 49 | files('coverage/'), 50 | clean(), 51 | karma(require('./conf/karma.dev').default) 52 | ); 53 | } 54 | 55 | export const prepush = test; 56 | -------------------------------------------------------------------------------- /conf/karma.common.js: -------------------------------------------------------------------------------- 1 | import { LOG_WARN } from 'karma/lib/constants'; 2 | 3 | import webpackTestConfig from './webpack.test'; 4 | 5 | export default { 6 | port: 3001, 7 | webpackPort: 3002, 8 | colors: true, 9 | basePath: './', 10 | files: [ 11 | 'test/index.js' 12 | ], 13 | // https://npmjs.org/browse/keyword/karma-preprocessor 14 | preprocessors: { 15 | 'test/index.js': 'webpack' 16 | }, 17 | // https://npmjs.org/browse/keyword/karma-adapter 18 | frameworks: [ 'mocha' ], 19 | webpack: webpackTestConfig, 20 | webpackMiddleware: { 21 | noInfo: true, 22 | quiet: true 23 | }, 24 | // https://npmjs.org/browse/keyword/karma-reporter 25 | reporters: [ 'mocha', 'coverage' ], 26 | // https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md 27 | coverageReporter: { 28 | dir: 'coverage/', 29 | reporters: [ 30 | { 31 | type: 'html' 32 | }, 33 | { 34 | type: 'text-summary' 35 | }, 36 | { 37 | type: 'lcovonly', subdir: '.' 38 | } 39 | ] 40 | }, 41 | logLevel: LOG_WARN, 42 | browsers: [ 'jsdom' ], 43 | browserNoActivityTimeout: 30000, // default 10 * 1000 44 | browserDisconnectTimeout: 10000, // default 2 * 1000 45 | browserDisconnectTolerance: 1 // default 0 46 | }; 47 | -------------------------------------------------------------------------------- /conf/webpack.build.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 4 | 5 | import webpackCommonConfig from './webpack.common'; 6 | 7 | export default { 8 | ...webpackCommonConfig, 9 | entry: { 10 | vendor: [ 11 | 'react', 12 | 'react-dom', 13 | 'rebem', 14 | 'rebem-core-components', 15 | 'rebem-theme-reset' 16 | ], 17 | app: './src/index' 18 | }, 19 | output: { 20 | path: path.resolve('./build/'), 21 | filename: 'js/[name].js' 22 | }, 23 | module: { 24 | preLoaders: webpackCommonConfig.module.preLoaders, 25 | loaders: [ 26 | ...webpackCommonConfig.module.loaders, 27 | { 28 | test: /\.css$/, 29 | loader: ExtractTextPlugin.extract( 30 | 'style', 31 | 'css?-minimize' + 32 | '!postcss' 33 | ) 34 | }, 35 | { 36 | test: /\.less$/, 37 | loader: ExtractTextPlugin.extract( 38 | 'style', 39 | 'css?-minimize' + 40 | '!postcss' + 41 | '!less' 42 | ) 43 | } 44 | ] 45 | }, 46 | plugins: [ 47 | ...webpackCommonConfig.plugins, 48 | new webpack.optimize.CommonsChunkPlugin('vendor', 'js/[name].js'), 49 | new ExtractTextPlugin('css/[name].css', { 50 | allChunks: true 51 | }), 52 | new webpack.optimize.DedupePlugin(), 53 | new webpack.optimize.OccurenceOrderPlugin() 54 | ] 55 | }; 56 | -------------------------------------------------------------------------------- /src/components/authentication/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import { blockFactory } from 'rebem'; 3 | 4 | import Button from '#button'; 5 | import Form from '#form'; 6 | import Heading from '#heading'; 7 | import Input from '#input'; 8 | import Notification from '#notification'; 9 | 10 | const block = 'authentication'; 11 | const Block = blockFactory(block); 12 | 13 | export default class Authentication extends Component { 14 | constructor(props) { 15 | super(props); 16 | 17 | this.state = { 18 | status: '' 19 | }; 20 | } 21 | 22 | handleFormSubmit = e => { 23 | e.preventDefault(); 24 | 25 | this.setState({ 26 | status: 'ready to go!' 27 | }); 28 | }; 29 | 30 | render() { 31 | return Block(this.props, 32 | Heading({ 33 | type: 'h2', 34 | mix: { 35 | block, 36 | elem: 'heading' 37 | } 38 | }, 'Login'), 39 | Notification({ 40 | mix: { 41 | block, 42 | elem: 'status', 43 | mods: { empty: this.state.status === '' } 44 | } 45 | }, this.state.status), 46 | Form({ mix: { block, elem: 'form' }, onSubmit: this.handleFormSubmit }, 47 | Block({ elem: 'form-row' }, 48 | Input({ placeholder: 'login' }) 49 | ), 50 | Block({ elem: 'form-row' }, 51 | Input({ placeholder: 'password', type: 'password' }) 52 | ), 53 | Block({ elem: 'form-row' }, 54 | Button({ type: 'submit', value: 'Submit' }) 55 | ) 56 | ) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/src/components/app/index.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { shallow } from 'rebem-enzyme'; 3 | 4 | import App from '#app'; 5 | 6 | // we import factory by default, but in tests we need class 7 | import Authentication from '#authentication?class'; 8 | import Heading from '#heading?class'; 9 | 10 | let renderedComponent = null; 11 | const block = 'app'; 12 | 13 | describe('App', function() { 14 | it('should exist', function() { 15 | expect(App).to.exist; 16 | }); 17 | 18 | describe('render', function() { 19 | beforeEach(function() { 20 | renderedComponent = shallow(App()); 21 | }); 22 | 23 | describe('header elem', function() { 24 | let headerElement = null; 25 | 26 | beforeEach(function() { 27 | headerElement = renderedComponent.findBEM({ block, elem: 'header' }); 28 | }); 29 | 30 | it('exists', function() { 31 | expect(headerElement).to.have.length(1); 32 | }); 33 | 34 | it('Heading component', function() { 35 | expect(headerElement.find(Heading)).to.have.length(1); 36 | }); 37 | }); 38 | 39 | describe('content elem', function() { 40 | let contentElement = null; 41 | 42 | beforeEach(function() { 43 | contentElement = renderedComponent.findBEM({ block, elem: 'content' }); 44 | }); 45 | 46 | it('exists', function() { 47 | expect(contentElement).to.have.length(1); 48 | }); 49 | 50 | it('Authentication component', function() { 51 | expect(contentElement.find(Authentication)).to.have.length(1); 52 | }); 53 | }); 54 | 55 | it('footer elem', function() { 56 | expect(renderedComponent.findBEM({ block, elem: 'footer' })).to.have.length(1); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # reBEM Starter Kit 2 | 3 | [![maintenance](https://img.shields.io/badge/maintained-no-red.svg?style=flat-square)](http://unmaintained.tech) 4 | [![travis](http://img.shields.io/travis/rebem/starter-kit.svg?style=flat-square)](https://travis-ci.org/rebem/starter-kit) 5 | [![deps](https://img.shields.io/gemnasium/rebem/starter-kit.svg?style=flat-square)](https://gemnasium.com/rebem/starter-kit) 6 | 7 | React starter kit based on [reBEM](https://github.com/rebem) stack. 8 | 9 | 10 | 11 | ## Structure 12 | 13 | * `build/` – output folder 14 | * `conf/` – configs 15 | * `tasks.js` – [start](https://github.com/start-runner/start) tasks 16 | * `src/index.js` – main entry point 17 | * `src/components` – app [layer](#layers) components 18 | * `src/theme-bootstrap` – bootstrap [layer](#layers) components 19 | * `test` — tests 20 | 21 | ## Usage 22 | 23 | ### install 24 | 25 | ``` 26 | npm i 27 | ``` 28 | 29 | ### dev 30 | 31 | ``` 32 | npm start dev 33 | open http://localhost:3000/webpack-dev-server/ 34 | ``` 35 | 36 | ### build 37 | 38 | ``` 39 | npm start build 40 | open build/index.html 41 | ``` 42 | 43 | ### Layers 44 | 45 | There are four [reBEM layers](https://github.com/rebem/layers-loader) in this starter kit: 46 | 47 | 1. [reBEM core components](https://github.com/rebem/core-components) 48 | 2. [reBEM reset theme](https://github.com/rebem/theme-reset) 49 | 3. Simple inline theme based on [Bootstrap 3](https://github.com/twbs/bootstrap). It'll give you the basic understanding on how to create your own themes. 50 | 4. App itself 51 | 52 | Feel free to create and use your own. 53 | 54 | ## Testing 55 | 56 | Tests are preconfigured with [karma](https://karma-runner.github.io/0.13/index.html), [mocha](https://mochajs.org/) and [chai](http://chaijs.com/). Testing stack also includes some useful helpers: 57 | - [enzyme](https://github.com/airbnb/enzyme) — great testing library for React 58 | - [rebem-enzyme](https://github.com/rebem/enzyme) — BEM addons for Enzyme 59 | - [chai-bem](https://github.com/mistadikay/chai-bem) — Chai assertions for BEM class names 60 | 61 | ### run tests in TDD mode 62 | 63 | ``` 64 | npm start tdd 65 | ``` 66 | 67 | ### run tests once 68 | 69 | ``` 70 | npm start test 71 | ``` 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rebem-starter-kit", 3 | "version": "0.0.0", 4 | "description": "reBEM Starter Kit", 5 | "homepage": "https://github.com/rebem/starter-kit", 6 | "repository": "rebem/starter-kit", 7 | "maintainers": [ 8 | "Kir Belevich (https://github.com/deepsweet)", 9 | "Denis Koltsov (https://github.com/mistadikay)" 10 | ], 11 | "dependencies": { 12 | "webpack": "1.12.x", 13 | "style-loader": "0.13.x", 14 | "css-loader": "0.23.x", 15 | "postcss-loader": "0.8.x", 16 | "autoprefixer": "6.3.x", 17 | "less": "2.6.x", 18 | "less-loader": "2.2.x", 19 | "json-loader": "0.5.x", 20 | "extract-text-webpack-plugin": "1.0.x", 21 | "html-webpack-plugin": "2.15.x", 22 | 23 | "babel-preset-es2015": "6.6.x", 24 | "babel-preset-stage-1": "6.5.x", 25 | "babel-plugin-transform-runtime": "6.6.x", 26 | "babel-loader": "6.2.x", 27 | 28 | "bootstrap": "3.3.6", 29 | 30 | "react": "0.14.x", 31 | "react-dom": "0.14.x", 32 | "rebem": "0.9.x", 33 | "rebem-layers-loader": "0.5.x", 34 | "rebem-core-components": "0.3.x", 35 | "rebem-theme-reset": "0.2.x" 36 | }, 37 | "devDependencies": { 38 | "start": "4.x.x", 39 | "start-babel-cli": "1.x.x", 40 | "start-pretty-reporter": "0.x.x", 41 | "start-env": "0.x.x", 42 | "start-files": "0.x.x", 43 | "start-clean": "1.x.x", 44 | "start-eslint": "2.x.x", 45 | "start-webpack": "0.x.x", 46 | "start-karma": "0.x.x", 47 | 48 | "babel-eslint": "6.0.x", 49 | "eslint-plugin-babel": "3.1.x", 50 | "eslint-plugin-react": "4.2.x", 51 | "eslint-config-rebem": "1.1.x", 52 | "babel-preset-react-hmre": "1.1.x", 53 | 54 | "enzyme": "2.2.x", 55 | "chai-enzyme": "0.4.x", 56 | "rebem-enzyme": "0.3.x", 57 | "react-addons-test-utils": "0.14.x", 58 | 59 | "webpack-dev-server": "1.14.x", 60 | "babel-istanbul-loader": "0.1.x", 61 | "mocha": "2.4.x", 62 | "chai": "3.5.x", 63 | "chai-spies": "0.7.x", 64 | "chai-bem": "1.4.x", 65 | "karma-mocha": "0.2.x", 66 | "karma-mocha-reporter": "2.0.x", 67 | "karma-jsdom-launcher": "3.0.x", 68 | "karma-webpack": "1.7.x", 69 | "karma-coverage": "0.5.x", 70 | "karma-clear-screen-reporter": "1.0.x", 71 | 72 | "husky": "0.11.x" 73 | }, 74 | "scripts": { 75 | "start": "start-runner ./tasks.js", 76 | "prepush": "npm start prepush" 77 | }, 78 | "engines": { 79 | "node": ">=0.12", 80 | "npm": ">=2.7" 81 | }, 82 | "license": "MIT" 83 | } 84 | -------------------------------------------------------------------------------- /conf/webpack.common.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 4 | import autoprefixer from 'autoprefixer'; 5 | 6 | export default { 7 | cache: true, 8 | stats: { 9 | colors: true, 10 | reasons: false 11 | }, 12 | output: { 13 | pathinfo: true 14 | }, 15 | resolve: { 16 | alias: { 17 | '~': path.resolve('src/') 18 | } 19 | }, 20 | module: { 21 | preLoaders: [ 22 | { 23 | test: /\.js$/, 24 | loader: 'rebem-layers', 25 | query: { 26 | layers: [ 27 | require('rebem-core-components'), 28 | require('rebem-theme-reset'), 29 | require('../src/theme-bootstrap'), 30 | { 31 | path: path.resolve('src/components/'), 32 | files: { 33 | main: 'index.js', 34 | styles: 'styles.less' 35 | }, 36 | importFactory: true 37 | } 38 | ], 39 | consumers: [ 40 | path.resolve('src/index') 41 | ] 42 | } 43 | }, 44 | { 45 | test: /\.js$/, 46 | exclude: [ 47 | path.resolve('node_modules/') 48 | ], 49 | loader: 'babel', 50 | query: { 51 | cacheDirectory: true 52 | } 53 | } 54 | ], 55 | loaders: [ 56 | { 57 | test: /\.json$/, 58 | loader: 'json' 59 | } 60 | ] 61 | }, 62 | postcss() { 63 | return [ 64 | autoprefixer({ 65 | browsers: [ 66 | 'last 2 Chrome versions', 67 | 'last 2 Firefox versions', 68 | 'last 2 Safari versions', 69 | 'last 2 Explorer versions' 70 | ] 71 | }) 72 | ]; 73 | }, 74 | plugins: [ 75 | new webpack.DefinePlugin({ 76 | 'process.env': { 77 | NODE_ENV: JSON.stringify(process.env.NODE_ENV) 78 | } 79 | }), 80 | new HtmlWebpackPlugin({ 81 | template: 'src/assets/index.html' 82 | }) 83 | ] 84 | }; 85 | -------------------------------------------------------------------------------- /conf/webpack.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import webpackCommonConfig from './webpack.common'; 4 | 5 | const testingSources = [ 6 | path.resolve('src/components/') 7 | ]; 8 | 9 | export default { 10 | ...webpackCommonConfig, 11 | resolve: { 12 | ...webpackCommonConfig.resolve, 13 | alias: { 14 | ...webpackCommonConfig.resolve.alias, 15 | test: path.resolve('test') 16 | } 17 | }, 18 | module: { 19 | preLoaders: [ 20 | { 21 | test: /\.js$/, 22 | loader: 'rebem-layers', 23 | query: { 24 | layers: [ 25 | require('rebem-core-components'), 26 | require('rebem-theme-reset'), 27 | require('../src/theme-bootstrap'), 28 | { 29 | path: path.resolve('src/components/'), 30 | files: { 31 | main: 'index.js', 32 | styles: 'styles.less' 33 | }, 34 | importFactory: true 35 | } 36 | ], 37 | consumers: [ 38 | path.resolve('src/index.js'), 39 | path.resolve('test/') 40 | ] 41 | } 42 | }, 43 | { 44 | test: /\.js$/, 45 | include: testingSources, 46 | loader: 'babel-istanbul', 47 | query: { 48 | cacheDirectory: true 49 | } 50 | }, 51 | { 52 | test: /\.js$/, 53 | exclude: [ 54 | ...testingSources, 55 | path.resolve('node_modules/') 56 | ], 57 | loader: 'babel', 58 | query: { 59 | cacheDirectory: true 60 | } 61 | } 62 | ], 63 | loaders: [ 64 | ...webpackCommonConfig.module.loaders, 65 | { 66 | test: /\.css$/, 67 | loaders: [ 68 | 'style', 69 | 'css?-minimize', 70 | 'postcss' 71 | ] 72 | }, 73 | { 74 | test: /\.less$/, 75 | loaders: [ 76 | 'style', 77 | 'css?-minimize', 78 | 'postcss', 79 | 'less' 80 | ] 81 | } 82 | ] 83 | } 84 | }; 85 | --------------------------------------------------------------------------------