Skip to content

Use Alt instead of pure Flux #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 58 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# React seed [![Build Status](https://travis-ci.org/badsyntax/react-seed.svg?branch=master)](https://travis-ci.org/badsyntax/react-seed)
# React seed [![Circle CI](https://circleci.com/gh/continuata/react-seed.svg?style=svg)](https://circleci.com/gh/continuata/react-seed)

A boilerplate for building React apps with ES6 and webpack.

Expand All @@ -12,6 +12,7 @@ A boilerplate for building React apps with ES6 and webpack.
* Basic flux architecture with app actions, stores and example web API usage
* React router ([feature/react-router](https://github.com/badsyntax/react-seed/tree/feature/react-router))
* Material UI ([feature/material-ui](https://github.com/badsyntax/react-seed/tree/feature/material-ui))
* Standard JS for linting

## Getting started

Expand Down Expand Up @@ -40,32 +41,30 @@ git clone --depth=1 https://github.com/badsyntax/react-seed.git my-project
```js
// Filename: Menu.jsx

'use strict';
import styles from './_Menu.scss'
import React from 'react'
import MenuItem from './MenuItem'

import styles from './_Menu.scss';
import React from 'react';
import MenuItem from './MenuItem';

let { Component, PropTypes } = React;
let { Component, PropTypes } = React

export default class Menu extends Component {

static defaultProps = {
items: []
};
}

static propTypes = {
items: PropTypes.array.isRequired
};
}

render() {
render () {
return (
<ul className={styles.menu}>
{this.props.items.map((item) => {
return (<MenuItem item={item} />);
{this.props.items.map((item, key) => {
return (<MenuItem item={item} key={key} />)
}, this)}
</ul>
);
)
}
}
```
Expand All @@ -74,70 +73,71 @@ export default class Menu extends Component {

```js
// Filename: __tests__/Menu-test.jsx
import React from 'react/addons'
import { expect } from 'chai'

'use strict';

import React from 'react/addons';
import { expect } from 'chai';

import Menu from '../Menu.jsx';
import MenuItem from '../MenuItem.jsx';

// Here we create a mocked MenuItem component.
class MockedMenuItem extends MenuItem {
render() {
return (
<li className={'mocked-menu-item'}>{this.props.item.label}</li>
);
}
}

// Here we set the mocked MenuItem component.
Menu.__Rewire__('MenuItem', MockedMenuItem);
import Menu from '../Menu.jsx'

describe('Menu', () => {

let { TestUtils } = React.addons;
let { TestUtils } = React.addons

let menuItems = [
{ id: 1, label: 'Option 1' },
{ id: 2, label: 'Option 2' }
];

let menu = TestUtils.renderIntoDocument(
<Menu items={menuItems} />
);
let menuElem = React.findDOMNode(menu);
let items = menuElem.querySelectorAll('li');

it('Should render the menu items', () => {
expect(items.length).to.equal(2);
});

it('Should render the menu item labels', () => {
Array.prototype.forEach.call(items, (item, i) => {
expect(item.textContent.trim()).to.equal(menuItems[i].label);
});
]

describe('Rendering', () => {
// Here we create a mocked MenuItem component.
let menu = TestUtils.renderIntoDocument(
<Menu items={menuItems} />
)
let menuElem = React.findDOMNode(menu)
let items = menuElem.querySelectorAll('li')

it('Should render the menu items', () => {
expect(items.length).to.equal(2)
})

it('Should render the menu item labels', () => {
Array.prototype.forEach.call(items, (item, i) => {
expect(item.textContent.trim()).to.equal(menuItems[i].label)
})
})
})

it('Should render the mocked menu item', () => {
expect(items[0].className).to.equal('mocked-menu-item');
});
});

describe('Events', (done) => {
// Example of simulating browser events.
it('Should handle click events', () => {
let menu = TestUtils.renderIntoDocument(
<Menu items={menuItems} />
)
let menuElem = React.findDOMNode(menu)
let items = menuElem.querySelectorAll('li')
let node = items[0].querySelector('a')

window.onAlert = function (alertText) {
expect(alertText).to.equal("ALERT: 'You clicked Option 1'")
done()
}

TestUtils.Simulate.click(node)
})
})
})
```

## Sass, CSS & webpack
## Sass, Stylus, CSS & webpack

`import` Sass and CSS files from within your JavaScript component files:
`import` Sass, Stylus and CSS files from within your JavaScript component files:

```js
// Filename: app.jsx
import 'normalize.css/normalize.css';
import styles from './scss/app.scss';
import styles from './styl/app.styl';
```

* **Note:** If you're importing component Sass files from within your JavaScript component files, then each sass file will be compiled as part of a different compile process, and thus you cannot share global references. See [this issue](https://github.com/jtangelder/sass-loader/issues/105) for more information.
* **Note:** If you're importing component Sass, Styl files from within your JavaScript component files, then each sass file will be compiled as part of a different compile process, and thus you cannot share global references. See [this issue](https://github.com/jtangelder/sass-loader/issues/105) for more information.
* Sass include paths can be adjusted in the `webpack/loaders.js` file.
* All CSS (compiled or otherwise) is run through Autoprefixer and style-loader. Any images/fonts etc referenced in the CSS will be copied to the build dir.
* CSS files are combined in the order in which they are imported in JavaScript, thus
Expand Down
29 changes: 7 additions & 22 deletions app/actions/AppActions.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
import AppDispatcher from '../dispatcher/AppDispatcher';
import WebAPI from '../util/WebAPI';
import alt from '../alt'

import {
ITEMS_GET_SUCCESS,
ITEMS_GET_ERROR
} from '../constants/AppConstants';

export default {
getItems() {
WebAPI.getItems()
.then((items) => {
AppDispatcher.dispatch({
actionType: ITEMS_GET_SUCCESS,
items: items
});
})
.catch(() => {
AppDispatcher.dispatch({
actionType: ITEMS_GET_ERROR
});
});
class AppActions {
constructor () {
this.generateActions('getItems')
}
};
}

export default alt.createActions(AppActions)
2 changes: 2 additions & 0 deletions app/alt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Alt from 'alt'
export default new Alt()
23 changes: 12 additions & 11 deletions app/app.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import './favicon.ico';
import './index.html';
import 'babel-core/polyfill';
import 'normalize.css/normalize.css';
import './scss/app.scss';
import './favicon.ico'
import './index.html'
import 'babel-core/polyfill'
import 'normalize.css/normalize.css'
import './scss/app.scss'

import React from 'react';
import App from './components/App/App';
import React from 'react'
import Router from 'react-router'

React.render(
<App />,
document.getElementById('app')
);
import routes from './routes'

Router.run(routes, function (Handler) {
React.render(React.createElement(Handler), document.body)
})
6 changes: 3 additions & 3 deletions app/app.tests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'babel-core/polyfill';
import 'babel-core/polyfill'

let context = require.context('.', true, /-test\.jsx?$/);
context.keys().forEach(context);
let context = require.context('.', true, /-test\.jsx?$/)
context.keys().forEach(context)
45 changes: 16 additions & 29 deletions app/components/App/App.jsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,27 @@
import styles from './_App.scss';
import styles from './_App.scss'

import React from 'react';
import AppActions from '../../actions/AppActions';
import ItemsStore from '../../stores/ItemsStore';
import Body from '../Body/Body';
import Footer from '../Footer/Footer';

function getAppState() {
return {
items: ItemsStore.getAll()
};
}
import React from 'react'
import { RouteHandler } from 'react-router'
import AppActions from '../../actions/AppActions'
import ItemsStore from '../../stores/ItemsStore'
import Footer from '../Footer/Footer'

export default class App extends React.Component {

state = getAppState()

componentDidMount() {
ItemsStore.addChangeListener(this.onChange);
AppActions.getItems();
}

componentWillUnmount() {
ItemsStore.removeChangeListener(this.onChange);
}

onChange = () => {
this.setState(getAppState());
constructor () {
super()
this.state = {items: []}
AppActions.getItems()
ItemsStore.listen((data) => {
this.setState({items: data.items})
})
}

render() {
render () {
return (
<div className={styles.app}>
<Body items={this.state.items} />
<RouteHandler items={this.state.items} />
<Footer />
</div>
);
)
}
}
19 changes: 10 additions & 9 deletions app/components/Body/Body.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import styles from './_Body.scss';
import React from 'react';
import Menu from '../Menu/Menu';
import styles from './_Body.scss'

let { PropTypes } = React;
import React from 'react'
import Menu from '../Menu/Menu'

let { PropTypes } = React

export default class Body extends React.Component {

static defaultProps = {
items: []
};
}

static propTypes = {
items: PropTypes.array.isRequired
};
}

render() {
render () {
return (
<div className={styles.body}>
<h1 className={styles.header}>React Seed</h1>
<p>This is an example seed app, powered by React, ES6 &amp; webpack.</p>
<p>This is an example seed app, powered by React, ES6, Alt &amp; webpack.</p>
<p>Here is some example data:</p>
<Menu items={this.props.items} />
<h2>Getting started</h2>
Expand All @@ -29,6 +30,6 @@ export default class Body extends React.Component {
<li>Change the data rendered above. Look in: <pre>./app/components/App/App.jsx</pre> Understand how data flows from the actions into the stores and then into the Body component.</li>
</ol>
</div>
);
)
}
}
10 changes: 5 additions & 5 deletions app/components/Footer/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import styles from './_Footer.scss';
import React from 'react';
import styles from './_Footer.scss'
import React from 'react'

export default class Footer extends React.Component {
render() {
var year = (new Date()).getFullYear();
render () {
var year = (new Date()).getFullYear()
return (
<footer className={styles.footer}>
&copy; Your Company&nbsp;{year}
</footer>
);
)
}
}
18 changes: 9 additions & 9 deletions app/components/Footer/__tests__/Footer-test.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react/addons';
import Footer from '../Footer.jsx';
import { expect } from 'chai';
import React from 'react/addons'
import Footer from '../Footer.jsx'
import { expect } from 'chai'

let { TestUtils } = React.addons;
let { TestUtils } = React.addons

describe('Footer', () => {
it('Should have the correct footer element', () => {
let footer = TestUtils.renderIntoDocument(
<Footer />
);
let footerElem = React.findDOMNode(footer);
expect(footerElem.tagName.toLowerCase()).to.equal('footer');
});
});
)
let footerElem = React.findDOMNode(footer)
expect(footerElem.tagName.toLowerCase()).to.equal('footer')
})
})
Loading