mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-28 00:17:12 +01:00
Update input field for feature toggles
This commit is contained in:
parent
9b7b487fdc
commit
c867d602e8
@ -2,8 +2,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import Input from 'react-toolbox/lib/input';
|
||||
import Button from 'react-toolbox/lib/button';
|
||||
import Switch from 'react-toolbox/lib/switch';
|
||||
import SelectStrategies from './strategies-for-toggle-container';
|
||||
import SelectedStrategies from './selected-strategies';
|
||||
import StrategiesSection from './strategies-section-container';
|
||||
|
||||
class AddFeatureToggleComponent extends Component {
|
||||
|
||||
@ -20,6 +19,7 @@ class AddFeatureToggleComponent extends Component {
|
||||
setValue,
|
||||
addStrategy,
|
||||
removeStrategy,
|
||||
updateStrategy,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
editmode = false,
|
||||
@ -59,23 +59,11 @@ class AddFeatureToggleComponent extends Component {
|
||||
<br />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div>
|
||||
<h5 style={{
|
||||
borderBottom: '1px solid rgba(0, 0, 0, 0.12)',
|
||||
paddingBottom: '5px',
|
||||
marginBottom: '10px',
|
||||
}}>Strategies:</h5>
|
||||
</div>
|
||||
|
||||
<SelectedStrategies
|
||||
configuredStrategies={configuredStrategies}
|
||||
removeStrategy={removeStrategy} />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SelectStrategies addStrategy={addStrategy} />
|
||||
</section>
|
||||
<StrategiesSection
|
||||
configuredStrategies={configuredStrategies}
|
||||
addStrategy={addStrategy}
|
||||
updateStrategy={updateStrategy}
|
||||
removeStrategy={removeStrategy} />
|
||||
|
||||
<br />
|
||||
|
||||
@ -89,13 +77,13 @@ class AddFeatureToggleComponent extends Component {
|
||||
};
|
||||
|
||||
AddFeatureToggleComponent.propTypes = {
|
||||
strategies: PropTypes.array.required,
|
||||
input: PropTypes.object,
|
||||
setValue: PropTypes.func.required,
|
||||
addStrategy: PropTypes.func.required,
|
||||
removeStrategy: PropTypes.func.required,
|
||||
onSubmit: PropTypes.func.required,
|
||||
onCancel: PropTypes.func.required,
|
||||
setValue: PropTypes.func.isRequired,
|
||||
addStrategy: PropTypes.func.isRequired,
|
||||
removeStrategy: PropTypes.func.isRequired,
|
||||
updateStrategy: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
editmode: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,7 @@ const prepare = (methods, dispatch) => {
|
||||
);
|
||||
|
||||
methods.onCancel = () => {
|
||||
debugger;
|
||||
window.history.back();
|
||||
};
|
||||
|
||||
@ -25,6 +26,10 @@ const prepare = (methods, dispatch) => {
|
||||
methods.pushToList('strategies', v);
|
||||
};
|
||||
|
||||
methods.updateStrategy = (v, n) => {
|
||||
methods.updateInList('strategies', v, n);
|
||||
};
|
||||
|
||||
methods.removeStrategy = (v) => {
|
||||
methods.removeFromList('strategies', v);
|
||||
};
|
||||
|
@ -0,0 +1,40 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import ConfigureStrategy from './configure-strategy';
|
||||
class ConfigureStrategies extends React.Component {
|
||||
|
||||
static propTypes () {
|
||||
return {
|
||||
strategies: PropTypes.array.isRequired,
|
||||
configuredStrategies: PropTypes.array.isRequired,
|
||||
updateStrategy: PropTypes.func.isRequired,
|
||||
removeStrategy: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
strategies,
|
||||
configuredStrategies,
|
||||
} = this.props;
|
||||
|
||||
if (!configuredStrategies || configuredStrategies.length === 0) {
|
||||
return <i>No strategies added</i>;
|
||||
}
|
||||
|
||||
const blocks = configuredStrategies.map((strat, i) => (
|
||||
<ConfigureStrategy
|
||||
key={`${strat.name}-${i}`}
|
||||
strategy={strat}
|
||||
removeStrategy={this.props.removeStrategy}
|
||||
updateStrategy={this.props.updateStrategy}
|
||||
strategyDefinition={strategies.find(s => s.name === strat.name)} />
|
||||
));
|
||||
return (
|
||||
<div>
|
||||
{blocks}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigureStrategies;
|
@ -0,0 +1,65 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Button, Input } from 'react-toolbox';
|
||||
|
||||
class ConfigureStrategies extends React.Component {
|
||||
|
||||
static propTypes () {
|
||||
return {
|
||||
strategy: PropTypes.object.isRequired,
|
||||
strategyDefinition: PropTypes.object.isRequired,
|
||||
updateStrategy: PropTypes.func.isRequired,
|
||||
removeStrategy: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
updateStrategy = (evt) => {
|
||||
evt.preventDefault();
|
||||
this.props.updateStrategy({
|
||||
name: this.state.selectedStrategy.name,
|
||||
parameters: this.state.parameters,
|
||||
});
|
||||
};
|
||||
|
||||
handleConfigChange = (key, value) => {
|
||||
const parameters = {};
|
||||
parameters[key] = value;
|
||||
|
||||
const updatedStrategy = Object.assign({}, this.props.strategy, { parameters });
|
||||
|
||||
this.props.updateStrategy(this.props.strategy, updatedStrategy);
|
||||
};
|
||||
|
||||
handleRemove = (evt) => {
|
||||
evt.preventDefault();
|
||||
this.props.removeStrategy(this.props.strategy);
|
||||
}
|
||||
|
||||
renderInputFields (strategyDefinition) {
|
||||
if (strategyDefinition.parametersTemplate) {
|
||||
return Object.keys(strategyDefinition.parametersTemplate).map(field => (
|
||||
<Input
|
||||
type="text"
|
||||
key={field}
|
||||
name={field}
|
||||
label={field}
|
||||
onChange={this.handleConfigChange.bind(this, field)}
|
||||
value={this.props.strategy.parameters[field]}
|
||||
/>
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const inputFields = this.renderInputFields(this.props.strategyDefinition);
|
||||
return (
|
||||
<div>
|
||||
<strong>{this.props.strategy.name}</strong>
|
||||
<Button title="Remove Strategy" onClick={this.handleRemove} icon="remove" floating accent mini />
|
||||
<p><i>{this.props.strategyDefinition.description}</i></p>
|
||||
{inputFields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigureStrategies;
|
@ -51,6 +51,10 @@ const prepare = (methods, dispatch) => {
|
||||
methods.removeFromList('strategies', v);
|
||||
};
|
||||
|
||||
methods.updateStrategy = (v, n) => {
|
||||
methods.updateInList('strategies', v, n);
|
||||
};
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import AddStrategy from './add-strategy';
|
||||
import { fetchStrategies } from '../../store/strategy-actions';
|
||||
|
||||
|
||||
export default connect((state) => ({
|
||||
strategies: state.strategies.get('list').toArray(),
|
||||
}), { fetchStrategies })(AddStrategy);
|
@ -1,101 +0,0 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import Input from 'react-toolbox/lib/input';
|
||||
import Button from 'react-toolbox/lib/button';
|
||||
|
||||
class SelectStrategies extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedStrategy: props.strategies[0],
|
||||
parameters: {},
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes () {
|
||||
return {
|
||||
strategies: PropTypes.array.isRequired,
|
||||
cancelConfig: PropTypes.func.isRequired,
|
||||
addStrategy: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.props.fetchStrategies();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
// this will fix async strategies list loading after mounted
|
||||
if (!this.state.selectedStrategy && nextProps.strategies.length > 0) {
|
||||
this.setState({ selectedStrategy: nextProps.strategies[0] });
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (evt) => {
|
||||
const strategyName = evt.target.value;
|
||||
const selectedStrategy = this.props.strategies.find(s => s.name === strategyName);
|
||||
this.setState({ selectedStrategy, parameters: {} });
|
||||
}
|
||||
|
||||
addStrategy = (evt) => {
|
||||
evt.preventDefault();
|
||||
this.props.addStrategy({
|
||||
name: this.state.selectedStrategy.name,
|
||||
parameters: this.state.parameters,
|
||||
});
|
||||
};
|
||||
|
||||
handleConfigChange = (key, value) => {
|
||||
const parameters = this.state.parameters;
|
||||
parameters[key] = value;
|
||||
this.setState({ parameters });
|
||||
};
|
||||
|
||||
renderInputFields (selectedStrategy) {
|
||||
if (selectedStrategy.parametersTemplate) {
|
||||
return Object.keys(selectedStrategy.parametersTemplate).map(field => (
|
||||
<Input
|
||||
type="text"
|
||||
key={field}
|
||||
name={field}
|
||||
label={field}
|
||||
onChange={this.handleConfigChange.bind(this, field)}
|
||||
value={this.state.parameters[field]}
|
||||
/>
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const strategies = this.props.strategies.map(s => (
|
||||
<option key={s.name} value={s.name}>{s.name}</option>
|
||||
));
|
||||
|
||||
const style = {
|
||||
backgroundColor: '#ECE',
|
||||
padding: '10px',
|
||||
};
|
||||
|
||||
const selectedStrategy = this.state.selectedStrategy || this.props.strategies[0];
|
||||
|
||||
if (!selectedStrategy) {
|
||||
return <div>Strategies loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<select value={selectedStrategy.name} onChange={this.handleChange}>
|
||||
{strategies}
|
||||
</select>
|
||||
|
||||
<p><strong>Description:</strong> {selectedStrategy.description}</p>
|
||||
|
||||
{this.renderInputFields(selectedStrategy)}
|
||||
|
||||
<Button icon="add" accent label="add strategy" onClick={this.addStrategy} />
|
||||
<Button label="cancel" onClick={this.props.cancelConfig} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectStrategies;
|
@ -1,47 +0,0 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import Chip from 'react-toolbox/lib/chip';
|
||||
import Avatar from 'react-toolbox/lib/avatar';
|
||||
|
||||
class SelectedStrategies extends React.Component {
|
||||
static propTypes () {
|
||||
return {
|
||||
configuredStrategies: PropTypes.array.isRequired,
|
||||
removeStrategy: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
renderName (strategy) {
|
||||
const parameters = strategy.parameters || {};
|
||||
const keys = Object.keys(parameters);
|
||||
if (keys.length === 0) {
|
||||
return <span>{strategy.name}</span>;
|
||||
}
|
||||
|
||||
const params = keys
|
||||
.map(param => `${param}="${strategy.parameters[param]}"`)
|
||||
.join('; ');
|
||||
return <span>{strategy.name} ({params})</span>;
|
||||
}
|
||||
|
||||
render () {
|
||||
const removeStrategy = this.props.removeStrategy;
|
||||
const configuredStrategies = this.props.configuredStrategies.map((s, index) => (
|
||||
<Chip
|
||||
style={{ marginRight: '10px', marginBottom: '10px' }}
|
||||
key={`${index}-${s.name}`}
|
||||
deletable
|
||||
onDeleteClick={() => removeStrategy(s)}
|
||||
>
|
||||
<Avatar icon="edit" />
|
||||
{this.renderName(s)}
|
||||
</Chip>
|
||||
));
|
||||
return (
|
||||
<div>
|
||||
{configuredStrategies.length > 0 ? configuredStrategies : <p>No activation strategies added</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectedStrategies;
|
@ -1,5 +1,5 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Button, Input } from 'react-toolbox';
|
||||
import { Button } from 'react-toolbox';
|
||||
|
||||
class AddStrategy extends React.Component {
|
||||
constructor (props) {
|
||||
@ -17,11 +17,6 @@ class AddStrategy extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
// TODO: move somewhere appropriate?
|
||||
this.props.fetchStrategies();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
// this will fix async strategies list loading after mounted
|
||||
if (!this.state.selectedStrategy && nextProps.strategies.length > 0) {
|
||||
@ -65,9 +60,7 @@ class AddStrategy extends React.Component {
|
||||
<select value={selectedStrategy.name} onChange={this.handleChange}>
|
||||
{strategies}
|
||||
</select>
|
||||
|
||||
<Button icon="add" accent flat label="add strategy" onClick={this.addStrategy} />
|
||||
|
||||
<p><strong>Description:</strong> {selectedStrategy.description}</p>
|
||||
</div>
|
||||
);
|
@ -1,50 +0,0 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import Button from 'react-toolbox/lib/button';
|
||||
|
||||
class AddStrategiesToToggle extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedStrategy: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes () {
|
||||
return {
|
||||
addStrategy: PropTypes.func.isRequired,
|
||||
strategies: PropTypes.array.isRequired,
|
||||
fetchStrategies: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
// TODO: move somewhere appropriate?
|
||||
this.props.fetchStrategies();
|
||||
}
|
||||
|
||||
|
||||
addStrategy = (strategy) => {
|
||||
this.setState({ selectedStrategy: undefined });
|
||||
this.props.addStrategy(strategy);
|
||||
}
|
||||
|
||||
|
||||
render () {
|
||||
const selectedStrategy = this.state.selectedStrategy || this.props.strategies[0];
|
||||
|
||||
const strategies = this.props.strategies.map(s => (
|
||||
<option key={s.name} value={s.name}>{s.name}</option>
|
||||
));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<select value={selectedStrategy.name} onChange={this.handleChange}>
|
||||
{strategies}
|
||||
</select>
|
||||
<Button icon="add" accent onClick={this.showConfigure}>Add strategy</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddStrategiesToToggle;
|
@ -1,8 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import AddStrategy from './add-strategy';
|
||||
import StrategiesSection from './strategies-section';
|
||||
import { fetchStrategies } from '../../store/strategy-actions';
|
||||
|
||||
|
||||
export default connect((state) => ({
|
||||
strategies: state.strategies.get('list').toArray(),
|
||||
}), { fetchStrategies })(AddStrategy);
|
||||
}), { fetchStrategies })(StrategiesSection);
|
@ -0,0 +1,44 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import ConfigureStrategies from './configure-strategies';
|
||||
import AddStrategy from './strategies-add';
|
||||
|
||||
const headerStyle = {
|
||||
borderBottom: '1px solid rgba(0, 0, 0, 0.12)',
|
||||
paddingBottom: '5px',
|
||||
marginBottom: '10px',
|
||||
};
|
||||
|
||||
class StrategiesSection extends React.Component {
|
||||
|
||||
static propTypes () {
|
||||
return {
|
||||
strategies: PropTypes.array.isRequired,
|
||||
addStrategy: PropTypes.func.isRequired,
|
||||
removeStrategy: PropTypes.func.isRequired,
|
||||
updateStrategy: PropTypes.func.isRequired,
|
||||
fetchStrategies: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.props.fetchStrategies();
|
||||
}
|
||||
|
||||
render () {
|
||||
if (!this.props.strategies || this.props.strategies.length === 0) {
|
||||
return <i>Loding available strategies</i>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h5 style={headerStyle}>Strategies:</h5>
|
||||
<ConfigureStrategies {...this.props} />
|
||||
<AddStrategy {...this.props} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StrategiesSection;
|
@ -4,6 +4,7 @@ import {
|
||||
createSet,
|
||||
createPop,
|
||||
createPush,
|
||||
createUp,
|
||||
createInit,
|
||||
} from '../store/input-actions';
|
||||
|
||||
@ -56,6 +57,10 @@ export function createActions ({ id, prepare = (v) => v }) {
|
||||
dispatch(createPop({ id: getId(id, ownProps), key, value }));
|
||||
},
|
||||
|
||||
updateInList (key, value, newValue) {
|
||||
dispatch(createUp({ id: getId(id, ownProps), key, value, newValue }));
|
||||
},
|
||||
|
||||
incValue (key) {
|
||||
dispatch(createInc({ id: getId(id, ownProps), key }));
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Router, Route, IndexRedirect, hashHistory } from 'react-router';
|
||||
import { Router, Route, IndexRedirect, useRouterHistory } from 'react-router';
|
||||
import { createHashHistory } from 'history';
|
||||
import { Provider } from 'react-redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
@ -16,6 +17,8 @@ import CreateStrategies from './page/strategies/create';
|
||||
import HistoryPage from './page/history';
|
||||
import Archive from './page/archive';
|
||||
|
||||
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const unleashStore = createStore(
|
||||
store,
|
||||
applyMiddleware(
|
||||
@ -25,7 +28,7 @@ const unleashStore = createStore(
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={unleashStore}>
|
||||
<Router history={hashHistory}>
|
||||
<Router history={appHistory}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="/features" />
|
||||
<Route path="/features" component={Features} />
|
||||
|
@ -3,6 +3,7 @@ export const actions = {
|
||||
INCREMENT_VALUE: 'INCREMENT_VALUE',
|
||||
LIST_PUSH: 'LIST_PUSH',
|
||||
LIST_POP: 'LIST_POP',
|
||||
LIST_UP: 'LIST_UP',
|
||||
CLEAR: 'CLEAR',
|
||||
INIT: 'INIT',
|
||||
};
|
||||
@ -12,6 +13,7 @@ export const createInc = ({ id, key }) => ({ type: actions.INCREMENT_VALUE, id,
|
||||
export const createSet = ({ id, key, value }) => ({ type: actions.SET_VALUE, id, key, value });
|
||||
export const createPush = ({ id, key, value }) => ({ type: actions.LIST_PUSH, id, key, value });
|
||||
export const createPop = ({ id, key, value }) => ({ type: actions.LIST_POP, id, key, value });
|
||||
export const createUp = ({ id, key, value, newValue }) => ({ type: actions.LIST_UP, id, key, value, newValue });
|
||||
export const createClear = ({ id }) => ({ type: actions.CLEAR, id });
|
||||
|
||||
export default actions;
|
||||
|
@ -48,6 +48,13 @@ function addToList (state, { id, key, value }) {
|
||||
return state.updateIn(id.concat([key]), (list) => list.push(value));
|
||||
}
|
||||
|
||||
function updateInList (state, { id, key, value, newValue }) {
|
||||
state = assertId(state, id);
|
||||
state = assertList(state, id, key);
|
||||
|
||||
return state.updateIn(id.concat([key]), (list) => list.set(list.indexOf(value), newValue));
|
||||
}
|
||||
|
||||
function removeFromList (state, { id, key, value }) {
|
||||
state = assertId(state, id);
|
||||
state = assertList(state, id, key);
|
||||
@ -74,6 +81,8 @@ const inputState = (state = getInitState(), action) => {
|
||||
return addToList(state, action);
|
||||
case actions.LIST_POP:
|
||||
return removeFromList(state, action);
|
||||
case actions.LIST_UP:
|
||||
return updateInList(state, action);
|
||||
case actions.CLEAR:
|
||||
return clear(state, action);
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user