mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-06 00:07:44 +01:00
Merge pull request #96 from finn-no/feature/react-router
Move to react-router.
This commit is contained in:
commit
f5f3421e3f
@ -52,6 +52,7 @@
|
|||||||
"nconf": "0.7.1",
|
"nconf": "0.7.1",
|
||||||
"pg": "4.3.0",
|
"pg": "4.3.0",
|
||||||
"react": "^0.13.1",
|
"react": "^0.13.1",
|
||||||
|
"react-router": "^0.13.2",
|
||||||
"reflux": "^0.2.5",
|
"reflux": "^0.2.5",
|
||||||
"reqwest": "^1.1.4",
|
"reqwest": "^1.1.4",
|
||||||
"webpack": "1.7.3",
|
"webpack": "1.7.3",
|
||||||
|
@ -1,52 +1,91 @@
|
|||||||
var React = require('react');
|
var React = require('react');
|
||||||
var TabView = require('./components/TabView');
|
var Router = require('react-router');
|
||||||
var Menu = require('./components/Menu');
|
var Menu = require('./components/Menu');
|
||||||
var UserStore = require('./stores/UserStore');
|
|
||||||
var ErrorMessages = require('./components/ErrorMessages');
|
var ErrorMessages = require('./components/ErrorMessages');
|
||||||
var initalizer = require('./stores/initalizer');
|
var initalizer = require('./stores/initalizer');
|
||||||
var LogEntriesComponent = React.createFactory(require('./components/log/LogEntriesComponent'));
|
var FeatureToggleStore = require('./stores/FeatureToggleStore');
|
||||||
var FeatureTogglesComponent = React.createFactory(require('./components/feature/FeatureTogglesComponent'));
|
var StrategyStore = require('./stores/StrategyStore');
|
||||||
var StrategiesComponent = React.createFactory(require('./components/strategy/StrategiesComponent'));
|
var ArchiveStore = require('./stores/ArchivedToggleStore');
|
||||||
var ArchiveFeatureComponent = React.createFactory(require('./components/feature/ArchiveFeatureComponent'));
|
var Link = Router.Link;
|
||||||
|
var RouteHandler = Router.RouteHandler;
|
||||||
UserStore.init();
|
|
||||||
|
|
||||||
var tabPanes = [
|
|
||||||
{
|
|
||||||
name: 'Feature Toggles',
|
|
||||||
slug: 'feature-toggles',
|
|
||||||
content: FeatureTogglesComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Strategies',
|
|
||||||
slug: 'strategies',
|
|
||||||
content: StrategiesComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Log",
|
|
||||||
slug: 'log',
|
|
||||||
content: LogEntriesComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Archive",
|
|
||||||
slug: 'archive',
|
|
||||||
content: ArchiveFeatureComponent
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
var UnleashApp = React.createClass({
|
var UnleashApp = React.createClass({
|
||||||
|
contextTypes: {
|
||||||
|
router: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
features: FeatureToggleStore.getFeatureToggles(),
|
||||||
|
strategies: StrategyStore.getStrategies(),
|
||||||
|
archivedFeatures: ArchiveStore.getArchivedToggles()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onFeatureToggleChange: function() {
|
||||||
|
this.setState({
|
||||||
|
features: FeatureToggleStore.getFeatureToggles()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onStrategiesChange: function() {
|
||||||
|
this.setState({
|
||||||
|
strategies: StrategyStore.getStrategies()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onArchiveChange: function() {
|
||||||
|
this.setState({
|
||||||
|
archivedFeatures: ArchiveStore.getArchivedToggles()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.unsubscribeFS = FeatureToggleStore.listen(this.onFeatureToggleChange);
|
||||||
|
this.unsubscribeSS = StrategyStore.listen(this.onStrategiesChange);
|
||||||
|
this.unsubscribeAS = ArchiveStore.listen(this.onArchiveChange);
|
||||||
|
},
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this.unsubscribeFS();
|
||||||
|
this.unsubscribeSS();
|
||||||
|
this.unsubscribeAS();
|
||||||
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
initalizer(30);
|
initalizer(30);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderLink: function(id, label) {
|
||||||
|
return (
|
||||||
|
<Link to={id} className="nav-element centerify" activeClassName="nav-active">
|
||||||
|
<span className="topbar-nav-svg-caption caption showbydefault no-break">{label}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Menu />
|
<Menu>
|
||||||
|
{this.renderLink("features", "Toggles")}
|
||||||
|
{this.renderLink("strategies", "Strategies")}
|
||||||
|
{this.renderLink("log", "Log")}
|
||||||
|
{this.renderLink("archive", "Archive")}
|
||||||
|
</Menu>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<ErrorMessages />
|
<ErrorMessages />
|
||||||
<TabView tabPanes={tabPanes} />
|
<div className="mod shadow mrn pan">
|
||||||
|
<div className="inner pan">
|
||||||
|
<div className="bd">
|
||||||
|
<RouteHandler
|
||||||
|
features={this.state.features}
|
||||||
|
strategies={this.state.strategies}
|
||||||
|
archivedFeatures={this.state.archivedFeatures}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,4 +93,5 @@ var UnleashApp = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = UnleashApp;
|
module.exports = UnleashApp;
|
||||||
|
@ -16,13 +16,8 @@ describe("FeatureForm", function () {
|
|||||||
{ name: "featureY" }
|
{ name: "featureY" }
|
||||||
];
|
];
|
||||||
|
|
||||||
Server.getArchivedFeatures.mockImplementation(function(cb) {
|
Component = TestUtils.renderIntoDocument(
|
||||||
cb(null, archivedToggles);
|
<FeatureArchive archivedFeatures={archivedToggles} />);
|
||||||
});
|
|
||||||
|
|
||||||
FeatureToggleStore.initStore(archivedToggles);
|
|
||||||
|
|
||||||
Component = TestUtils.renderIntoDocument(<FeatureArchive />);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
var React = require('react');
|
var React = require('react');
|
||||||
var UnleashApp = require('./UnleashApp');
|
var Router = require('react-router');
|
||||||
React.render(<UnleashApp />, document.getElementById('content'));
|
var UserStore = require('./stores/UserStore');
|
||||||
|
var routes = require('./routes');
|
||||||
|
|
||||||
|
UserStore.init();
|
||||||
|
|
||||||
|
Router.run(routes, function (Handler) {
|
||||||
|
React.render(<Handler/>, document.getElementById('content'));
|
||||||
|
});
|
||||||
|
@ -10,7 +10,7 @@ var Menu = React.createClass({
|
|||||||
<User />
|
<User />
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-level1 h4">
|
<div className="nav-level1 h4">
|
||||||
<a href="" className="homelink pln">
|
<a href="#" className="homelink pln">
|
||||||
<span className="topbar-nav-svg-home">
|
<span className="topbar-nav-svg-home">
|
||||||
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53M
|
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53M
|
||||||
y5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MjcuNDExIiBoZWlnaHQ9IjE2OS4z
|
y5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MjcuNDExIiBoZWlnaHQ9IjE2OS4z
|
||||||
@ -50,6 +50,7 @@ var Menu = React.createClass({
|
|||||||
unleash admin
|
unleash admin
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
var React = require('react');
|
|
||||||
|
|
||||||
var TabView = React.createClass({
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {tabPanes: []};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
var activeTab = this.props.tabPanes[0];
|
|
||||||
|
|
||||||
var userHash = window.location.hash;
|
|
||||||
if(userHash) {
|
|
||||||
userHash = userHash.split("#")[1];
|
|
||||||
this.props.tabPanes.forEach(function(pane) {
|
|
||||||
if(pane.slug === userHash) {
|
|
||||||
activeTab = pane;
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {activeTab: activeTab};
|
|
||||||
},
|
|
||||||
|
|
||||||
onChangeTab: function(tabPane) {
|
|
||||||
this.setState({activeTab: tabPane});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var tabNodes = this.props.tabPanes.map(function (tabPane) {
|
|
||||||
return (
|
|
||||||
<li key={tabPane.name} className=
|
|
||||||
{tabPane.name === this.state.activeTab.name ? "active": ""}>
|
|
||||||
<a href={"#" + tabPane.slug}
|
|
||||||
onClick={this.onChangeTab.bind(this, tabPane)}>
|
|
||||||
{tabPane.name}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ul className="tabs mbn">
|
|
||||||
{tabNodes}
|
|
||||||
</ul>
|
|
||||||
<div className="tab-content">
|
|
||||||
<div className="active">
|
|
||||||
<div className="mod shadow mrn pan">
|
|
||||||
<div className="inner pan">
|
|
||||||
<div className="bd">
|
|
||||||
{new this.state.activeTab.content()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = TabView;
|
|
@ -1,27 +1,7 @@
|
|||||||
var React = require("react");
|
var React = require("react");
|
||||||
var FeatureActions = require('../../stores/FeatureToggleActions');
|
var FeatureActions = require('../../stores/FeatureToggleActions');
|
||||||
var FeatureToggleStore = require('../../stores/ArchivedToggleStore');
|
|
||||||
|
|
||||||
var ArchiveFeatureComponent = React.createClass({
|
var ArchiveFeatureComponent = React.createClass({
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
archivedFeatures: FeatureToggleStore.getArchivedToggles()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onStoreChange: function() {
|
|
||||||
this.setState({
|
|
||||||
archivedFeatures: FeatureToggleStore.getArchivedToggles()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.unsubscribe = FeatureToggleStore.listen(this.onStoreChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this.unsubscribe();
|
|
||||||
},
|
|
||||||
|
|
||||||
onRevive: function(item) {
|
onRevive: function(item) {
|
||||||
FeatureActions.revive.triggerPromise(item);
|
FeatureActions.revive.triggerPromise(item);
|
||||||
@ -30,7 +10,8 @@ var ArchiveFeatureComponent = React.createClass({
|
|||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Archived feature toggles</h1>
|
<h1>Archived Feature Toggles</h1>
|
||||||
|
<hr />
|
||||||
<table className="outerborder man">
|
<table className="outerborder man">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -39,7 +20,7 @@ var ArchiveFeatureComponent = React.createClass({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.state.archivedFeatures.map(this.renderArchivedItem)}
|
{this.props.archivedFeatures.map(this.renderArchivedItem)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,29 +3,14 @@ var FeatureList = require('./FeatureList');
|
|||||||
var FeatureForm = require('./FeatureForm');
|
var FeatureForm = require('./FeatureForm');
|
||||||
var FeatureActions = require('../../stores/FeatureToggleActions');
|
var FeatureActions = require('../../stores/FeatureToggleActions');
|
||||||
var ErrorActions = require('../../stores/ErrorActions');
|
var ErrorActions = require('../../stores/ErrorActions');
|
||||||
var FeatureToggleStore = require('../../stores/FeatureToggleStore');
|
|
||||||
var StrategyStore = require('../../stores/StrategyStore');
|
|
||||||
|
|
||||||
var FeatureTogglesComponent = React.createClass({
|
var FeatureTogglesComponent = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
features: FeatureToggleStore.getFeatureToggles(),
|
|
||||||
createView: false
|
createView: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
onFeatureToggleChange: function() {
|
|
||||||
this.setState({
|
|
||||||
features: FeatureToggleStore.getFeatureToggles()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.unsubscribe = FeatureToggleStore.listen(this.onFeatureToggleChange);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this.unsubscribe();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateFeature: function (feature) {
|
updateFeature: function (feature) {
|
||||||
FeatureActions.update.triggerPromise(feature);
|
FeatureActions.update.triggerPromise(feature);
|
||||||
},
|
},
|
||||||
@ -52,16 +37,18 @@ var FeatureTogglesComponent = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<h1>Feature Toggles</h1>
|
||||||
|
|
||||||
{this.state.createView ? this.renderCreateView() : this.renderCreateButton()}
|
{this.state.createView ? this.renderCreateView() : this.renderCreateButton()}
|
||||||
|
|
||||||
<FeatureList
|
<FeatureList
|
||||||
features={this.state.features}
|
features={this.props.features}
|
||||||
onFeatureChanged={this.updateFeature}
|
onFeatureChanged={this.updateFeature}
|
||||||
onFeatureArchive={this.archiveFeature}
|
onFeatureArchive={this.archiveFeature}
|
||||||
onFeatureSubmit={this.createFeature}
|
onFeatureSubmit={this.createFeature}
|
||||||
onFeatureCancel={this.cancelNewFeature}
|
onFeatureCancel={this.cancelNewFeature}
|
||||||
onNewFeature={this.newFeature}
|
onNewFeature={this.newFeature}
|
||||||
strategies={StrategyStore.getStrategies()} />
|
strategies={this.props.strategies} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -70,7 +57,7 @@ var FeatureTogglesComponent = React.createClass({
|
|||||||
return <FeatureForm
|
return <FeatureForm
|
||||||
onCancel={this.cancelNewFeature}
|
onCancel={this.cancelNewFeature}
|
||||||
onSubmit={this.createFeature}
|
onSubmit={this.createFeature}
|
||||||
strategies={StrategyStore.getStrategies()} />;
|
strategies={this.props.strategies} />;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderCreateButton: function() {
|
renderCreateButton: function() {
|
||||||
|
@ -24,6 +24,7 @@ var LogEntriesComponent = React.createClass({
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<h1>Log</h1>
|
||||||
<hr />
|
<hr />
|
||||||
<LogEntryList events={this.state.events} />
|
<LogEntryList events={this.state.events} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,29 +1,15 @@
|
|||||||
var React = require('react');
|
var React = require('react');
|
||||||
var StrategyList = require('./StrategyList');
|
var StrategyList = require('./StrategyList');
|
||||||
var StrategyForm = require('./StrategyForm');
|
var StrategyForm = require('./StrategyForm');
|
||||||
var StrategyStore = require('../../stores/StrategyStore');
|
|
||||||
var StrategyActions = require('../../stores/StrategyActions');
|
var StrategyActions = require('../../stores/StrategyActions');
|
||||||
|
|
||||||
var StrategiesComponent = React.createClass({
|
var StrategiesComponent = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
createView: false,
|
createView: false
|
||||||
strategies: StrategyStore.getStrategies()
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
onStoreChange: function() {
|
|
||||||
this.setState({
|
|
||||||
strategies: StrategyStore.getStrategies()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.unsubscribe = StrategyStore.listen(this.onStoreChange);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this.unsubscribe();
|
|
||||||
},
|
|
||||||
|
|
||||||
onNewStrategy: function() {
|
onNewStrategy: function() {
|
||||||
this.setState({createView: true});
|
this.setState({createView: true});
|
||||||
},
|
},
|
||||||
@ -44,11 +30,12 @@ var StrategiesComponent = React.createClass({
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<h1>Activation Strategies</h1>
|
||||||
{this.state.createView ?
|
{this.state.createView ?
|
||||||
this.renderCreateView() : this.renderCreateButton()}
|
this.renderCreateView() : this.renderCreateButton()}
|
||||||
<hr />
|
<hr />
|
||||||
<StrategyList
|
<StrategyList
|
||||||
strategies={this.state.strategies}
|
strategies={this.props.strategies}
|
||||||
onRemove={this.onRemove} />
|
onRemove={this.onRemove} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
20
public/js/routes.jsx
Normal file
20
public/js/routes.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
var React = require('react');
|
||||||
|
var Router = require('react-router');
|
||||||
|
var UnleashApp = require('./UnleashApp');
|
||||||
|
var LogEntriesComponent = require('./components/log/LogEntriesComponent');
|
||||||
|
var FeatureTogglesComponent = require('./components/feature/FeatureTogglesComponent');
|
||||||
|
var StrategiesComponent = require('./components/strategy/StrategiesComponent');
|
||||||
|
var ArchiveFeatureComponent = require('./components/feature/ArchiveFeatureComponent');
|
||||||
|
var DefaultRoute = Router.DefaultRoute;
|
||||||
|
var Route = Router.Route;
|
||||||
|
|
||||||
|
var routes = (
|
||||||
|
<Route name="app" path="/" handler={UnleashApp}>
|
||||||
|
<Route name="strategies" handler={StrategiesComponent}/>
|
||||||
|
<Route name="log" handler={LogEntriesComponent}/>
|
||||||
|
<Route name="archive" handler={ArchiveFeatureComponent}/>
|
||||||
|
<DefaultRoute name="features" handler={FeatureTogglesComponent}/>
|
||||||
|
</Route>
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = routes;
|
Loading…
Reference in New Issue
Block a user