Changed option value to list

This commit is contained in:
jpellizzari
2017-03-22 17:13:57 -07:00
parent 828d50fba0
commit 454acdd999
4 changed files with 89 additions and 58 deletions

View File

@@ -1,9 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { changeTopologyOption } from '../actions/app-actions';
class TopologyOptionAction extends React.Component {
export default class TopologyOptionAction extends React.Component {
constructor(props, context) {
super(props, context);
@@ -13,13 +10,14 @@ class TopologyOptionAction extends React.Component {
onClick(ev) {
ev.preventDefault();
const { optionId, topologyId, item } = this.props;
this.props.changeTopologyOption(optionId, item.get('value'), topologyId);
this.props.onClick(optionId, item.get('value'), topologyId);
}
render() {
const { activeValue, item } = this.props;
const className = activeValue === item.get('value')
? 'topology-option-action topology-option-action-selected' : 'topology-option-action';
const className = activeValue.includes(item.get('value'))
? 'topology-option-action topology-option-action-selected'
: 'topology-option-action';
return (
<div className={className} onClick={this.onClick}>
{item.get('label')}
@@ -27,8 +25,3 @@ class TopologyOptionAction extends React.Component {
);
}
}
export default connect(
null,
{ changeTopologyOption }
)(TopologyOptionAction);

View File

@@ -4,8 +4,18 @@ import { connect } from 'react-redux';
import { getCurrentTopologyOptions } from '../utils/topology-utils';
import { activeTopologyOptionsSelector } from '../selectors/topology';
import TopologyOptionAction from './topology-option-action';
import { changeTopologyOption } from '../actions/app-actions';
class TopologyOptions extends React.Component {
constructor(props, context) {
super(props, context);
this.handleOptionClick = this.handleOptionClick.bind(this);
}
handleOptionClick(optionId, value, topologyId) {
this.props.changeTopologyOption(optionId, value, topologyId);
}
renderOption(option) {
const { activeOptions, topologyId } = this.props;
@@ -19,6 +29,7 @@ class TopologyOptions extends React.Component {
<div className="topology-option-wrapper">
{option.get('options').map(item => (
<TopologyOptionAction
onClick={this.handleOptionClick}
optionId={optionId}
topologyId={topologyId}
key={item.get('value')}
@@ -32,9 +43,10 @@ class TopologyOptions extends React.Component {
}
render() {
const { options } = this.props;
return (
<div className="topology-options">
{this.props.options && this.props.options.toIndexedSeq().map(
{options && options.toIndexedSeq().map(
option => this.renderOption(option))}
</div>
);
@@ -50,5 +62,6 @@ function mapStateToProps(state) {
}
export default connect(
mapStateToProps
mapStateToProps,
{ changeTopologyOption }
)(TopologyOptions);

View File

@@ -1,4 +1,6 @@
import { is, fromJS } from 'immutable';
import expect from 'expect';
import { TABLE_VIEW_MODE } from '../../constants/naming';
// Root reducer test suite using Jasmine matchers
import { constructEdgeId } from '../../utils/layouter-utils';
@@ -307,7 +309,16 @@ describe('RootReducer', () => {
expect(nextState.get('topologies').size).toBe(2);
expect(nextState.get('currentTopology').get('name')).toBe('Topo1');
expect(nextState.get('currentTopology').get('url')).toBe('/topo1');
expect(nextState.get('currentTopology').get('options').first().get('id')).toBe('option1');
expect(nextState.get('currentTopology').get('options').first().get('id')).toEqual(['option1']);
expect(nextState.getIn(['currentTopology', 'options']).toJS()).toEqual([{
id: 'option1',
defaultValue: 'off',
selectType: 'one',
options: [
{ value: 'on'},
{ value: 'off'}
]
}]);
});
it('get sub-topology', () => {
@@ -318,7 +329,7 @@ describe('RootReducer', () => {
expect(nextState.get('topologies').size).toBe(2);
expect(nextState.get('currentTopology').get('name')).toBe('topo 1 grouped');
expect(nextState.get('currentTopology').get('url')).toBe('/topo1-grouped');
expect(nextState.get('currentTopology').get('options')).toBeUndefined();
expect(nextState.get('currentTopology').get('options')).toNotExist();
});
// topology options
@@ -330,51 +341,49 @@ describe('RootReducer', () => {
// default options
expect(activeTopologyOptionsSelector(nextState).has('option1')).toBeTruthy();
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBeA('array');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['off']);
// turn on
nextState = reducer(nextState, ChangeTopologyOptionAction);
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['on']);
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['on']);
// turn off
nextState = reducer(nextState, ChangeTopologyOptionAction2);
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['off']);
// sub-topology should retain main topo options
nextState = reducer(nextState, ClickSubTopologyAction);
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['off']);
// other topology w/o options dont return options, but keep in app state
nextState = reducer(nextState, ClickTopology2Action);
expect(activeTopologyOptionsSelector(nextState)).toBeUndefined();
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
expect(activeTopologyOptionsSelector(nextState)).toNotExist();
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['off']);
});
it('changes topologyOptions for selectType "many"', () => {
const action = {
type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
it('adds/removes a topology option', () => {
const addAction = {
type: ActionTypes.ADD_TOPOLOGY_OPTION,
topologyId: 'services',
option: 'namespace',
value: ['scope', 'monitoring']
value: 'scope'
};
const removeAction = {
type: ActionTypes.REMOVE_TOPOLOGY_OPTION,
topologyId: 'services',
option: 'namespace',
value: 'scope'
};
let nextState = initialState;
nextState = reducer(nextState, {
type: ActionTypes.RECEIVE_TOPOLOGIES,
topologies
});
nextState = reducer(nextState, {
type: ActionTypes.CLICK_TOPOLOGY,
topologyId: 'services'
});
nextState = reducer(nextState, { type: ActionTypes.RECEIVE_TOPOLOGIES, topologies});
nextState = reducer(nextState, { type: ActionTypes.CLICK_TOPOLOGY, topologyId: 'services' });
nextState = reducer(nextState, action);
expect(activeTopologyOptionsSelector(nextState).toJS()).toEqual({
namespace: ['scope', 'monitoring'],
pseudo: 'hide'
});
nextState = reducer(nextState, addAction);
});
it('sets topology options from route', () => {
@@ -404,8 +413,8 @@ describe('RootReducer', () => {
nextState = reducer(nextState, RouteAction);
nextState = reducer(nextState, ReceiveTopologiesAction);
nextState = reducer(nextState, ClickTopologyAction);
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['off']);
});
// nodes delta
@@ -423,7 +432,7 @@ describe('RootReducer', () => {
it('shows nodes that were received', () => {
let nextState = initialState;
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
});
it('knows a route was set', () => {
@@ -439,11 +448,11 @@ describe('RootReducer', () => {
nextState = reducer(nextState, ClickNodeAction);
expect(nextState.get('selectedNodeId')).toBe('n1');
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
nextState = reducer(nextState, deSelectNode);
expect(nextState.get('selectedNodeId')).toBe(null);
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
});
it('keeps showing nodes on navigating back after node click', () => {
@@ -460,7 +469,7 @@ describe('RootReducer', () => {
RouteAction.state = {topologyId: 'topo1', selectedNodeId: null};
nextState = reducer(nextState, RouteAction);
expect(nextState.get('selectedNodeId')).toBe(null);
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
});
it('closes details when changing topologies', () => {
@@ -486,12 +495,12 @@ describe('RootReducer', () => {
it('resets topology on websocket reconnect', () => {
let nextState = initialState;
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
nextState = reducer(nextState, CloseWebsocketAction);
expect(nextState.get('websocketClosed')).toBeTruthy();
// keep showing old nodes
expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
expect(nextState.get('nodes').toJS()).toInclude(NODE_SET);
nextState = reducer(nextState, OpenWebsocketAction);
expect(nextState.get('websocketClosed')).toBeFalsy();

View File

@@ -87,15 +87,32 @@ export const initialState = makeMap({
zoomCache: makeMap(),
});
function calcSelectType(topology) {
const result = {
...topology,
options: topology.options && topology.options.map((option) => {
// Server doesn't return the `selectType` key unless the option is something other than `one`.
// Default to `one` if undefined, so the component doesn't have to handle this.
option.selectType = option.selectType || 'one';
return option;
})
};
if (topology.sub_topologies) {
result.sub_topologies = topology.sub_topologies.map(calcSelectType);
}
return result;
}
// adds ID field to topology (based on last part of URL path) and save urls in
// map for easy lookup
function processTopologies(state, nextTopologies) {
// filter out hidden topos
const visibleTopologies = filterHiddenTopologies(nextTopologies);
// set `selectType` field for topology and sub_topologies options (recursive).
const topologiesWithSelectType = visibleTopologies.map(calcSelectType);
// add IDs to topology objects in-place
const topologiesWithId = updateTopologyIds(visibleTopologies);
const topologiesWithId = updateTopologyIds(topologiesWithSelectType);
// cache URLs by ID
state = state.set('topologyUrlsById',
setTopologyUrlsById(state.get('topologyUrlsById'), topologiesWithId));
@@ -106,8 +123,7 @@ function processTopologies(state, nextTopologies) {
}
function setTopology(state, topologyId) {
state = state.set('currentTopology', findTopologyById(
state.get('topologies'), topologyId));
state = state.set('currentTopology', findTopologyById(state.get('topologies'), topologyId));
return state.set('currentTopologyId', topologyId);
}
@@ -118,7 +134,7 @@ function setDefaultTopologyOptions(state, topologyList) {
topology.get('options').forEach((option) => {
const optionId = option.get('id');
const defaultValue = option.get('defaultValue');
defaultOptions = defaultOptions.set(optionId, defaultValue);
defaultOptions = defaultOptions.set(optionId, [defaultValue]);
});
}
@@ -185,7 +201,7 @@ export function rootReducer(state = initialState, action) {
}
state = state.setIn(
['topologyOptions', topologyId, action.option],
action.value
[action.value]
);
}
return state;