Skip to content

Commit

Permalink
Topology filter overhaul, still needs backend support
Browse files Browse the repository at this point in the history
  • Loading branch information
davkal committed Mar 29, 2016
1 parent 430130c commit 88abeb7
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 105 deletions.
3 changes: 2 additions & 1 deletion client/app/scripts/charts/nodes-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,8 @@ export default class NodesChart extends React.Component {
scale: nodeScale,
margins: MARGINS,
forceRelayout: props.forceRelayout,
topologyId: this.props.topologyId
topologyId: this.props.topologyId,
topologyOptions: this.props.topologyOptions
};

const timedLayouter = timely(doLayout);
Expand Down
19 changes: 15 additions & 4 deletions client/app/scripts/charts/nodes-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ function fromGraphNodeId(encodedId) {
return encodedId.replace('<DOT>', '.');
}

function buildCacheIdFromOptions(options) {
if (options) {
let id = options.topologyId;
if (options.topologyOptions) {
id += JSON.stringify(options.topologyOptions);
}
return id;
}
return '';
}

/**
* Layout engine runner
* After the layout engine run nodes and edges have x-y-coordinates. Engine is
Expand Down Expand Up @@ -343,18 +354,18 @@ function copyLayoutProperties(layout, nodeCache, edgeCache) {
*/
export function doLayout(immNodes, immEdges, opts) {
const options = opts || {};
const topologyId = options.topologyId || 'noId';
const cacheId = buildCacheIdFromOptions(options);

// one engine and node and edge caches per topology, to keep renderings similar
if (!topologyCaches[topologyId]) {
topologyCaches[topologyId] = {
if (!topologyCaches[cacheId]) {
topologyCaches[cacheId] = {
nodeCache: makeMap(),
edgeCache: makeMap(),
graph: new dagre.graphlib.Graph({})
};
}

const cache = topologyCaches[topologyId];
const cache = topologyCaches[cacheId];
const cachedLayout = options.cachedLayout || cache.cachedLayout;
const nodeCache = options.nodeCache || cache.nodeCache;
const edgeCache = options.edgeCache || cache.edgeCache;
Expand Down
1 change: 1 addition & 0 deletions client/app/scripts/components/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export default class App extends React.Component {
highlightedEdgeIds={this.state.highlightedEdgeIds} detailsWidth={detailsWidth}
selectedNodeId={this.state.selectedNodeId} topMargin={topMargin}
forceRelayout={this.state.forceRelayout}
topologyOptions={this.state.activeTopologyOptions}
topologyId={this.state.currentTopologyId} />

<Sidebar>
Expand Down
13 changes: 9 additions & 4 deletions client/app/scripts/components/topology-option-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import React from 'react';
import { changeTopologyOption } from '../actions/app-actions';

export default class TopologyOptionAction extends React.Component {

constructor(props, context) {
super(props, context);
this.onClick = this.onClick.bind(this);
}

onClick(ev) {
ev.preventDefault();
changeTopologyOption(this.props.option, this.props.value, this.props.topologyId);
const { optionId, topologyId, item } = this.props;
changeTopologyOption(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';
return (
<span className="sidebar-item-action" onClick={this.onClick}>
{this.props.value}
</span>
<div className={className} onClick={this.onClick}>
{item.get('label')}
</div>
);
}
}
62 changes: 12 additions & 50 deletions client/app/scripts/components/topology-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,28 @@ import React from 'react';
import TopologyOptionAction from './topology-option-action';

export default class TopologyOptions extends React.Component {
renderAction(action, option, topologyId) {
return (
<TopologyOptionAction option={option} value={action} topologyId={topologyId} key={action} />
);
}

/**
* transforms a list of options into one sidebar-item.
* The sidebar text comes from the active option. the actions come from the
* remaining items.
*/
renderOption(items) {
let activeText;
let activeValue;
const actions = [];
const activeOptions = this.props.activeOptions;
const topologyId = this.props.topologyId;
const option = items.first().get('option');

// find active option value
if (activeOptions && activeOptions.has(option)) {
activeValue = activeOptions.get(option);
} else {
// get default value
items.forEach(item => {
if (item.get('default')) {
activeValue = item.get('value');
}
});
}

// render active option as text, add other options as actions
items.forEach(item => {
if (item.get('value') === activeValue) {
activeText = item.get('display');
} else {
actions.push(this.renderAction(item.get('value'), item.get('option'), topologyId));
}
}, this);
renderOption(option) {
const { activeOptions, topologyId } = this.props;
const optionId = option.get('id');
const activeValue = activeOptions && activeOptions.has(optionId)
? activeOptions.get(optionId) : option.get('defaultValue');

return (
<div className="sidebar-item" key={option}>
{activeText}
<span className="sidebar-item-actions">
{actions}
</span>
<div className="topology-option" key={optionId}>
<div className="topology-option-wrapper">
{option.get('options').map(item => <TopologyOptionAction
optionId={optionId} topologyId={topologyId} key={item.get('value')}
activeValue={activeValue} item={item} />)}
</div>
</div>
);
}

render() {
const options = this.props.options.map((items, optionId) => {
let itemsMap = items.map(item => item.set('option', optionId));
itemsMap = itemsMap.set('option', optionId);
return itemsMap;
});

return (
<div className="topology-options">
{options.toIndexedSeq().map(items => this.renderOption(items))}
{this.props.options.toIndexedSeq().map(option => this.renderOption(option))}
</div>
);
}
Expand Down
14 changes: 8 additions & 6 deletions client/app/scripts/stores/__tests__/app-store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,14 @@ describe('AppStore', () => {
topologies: [{
url: '/topo1',
name: 'Topo1',
options: {
option1: [
options: [{
id: 'option1',
defaultValue: 'off',
options: [
{value: 'on'},
{value: 'off', default: true}
{value: 'off'}
]
},
}],
stats: {
node_count: 1
},
Expand Down Expand Up @@ -165,13 +167,13 @@ describe('AppStore', () => {
});

it('get current topology', () => {
registeredCallback(ClickTopologyAction);
registeredCallback(ReceiveTopologiesAction);
registeredCallback(ClickTopologyAction);

expect(AppStore.getTopologies().size).toBe(2);
expect(AppStore.getCurrentTopology().get('name')).toBe('Topo1');
expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1');
expect(AppStore.getCurrentTopologyOptions().get('option1')).toBeDefined();
expect(AppStore.getCurrentTopologyOptions().first().get('id')).toBe('option1');
});

it('get sub-topology', () => {
Expand Down
12 changes: 5 additions & 7 deletions client/app/scripts/stores/app-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,11 @@ function setTopology(topologyId) {
function setDefaultTopologyOptions(topologyList) {
topologyList.forEach(topology => {
let defaultOptions = makeOrderedMap();
if (topology.has('options')) {
topology.get('options').forEach((items, option) => {
items.forEach(item => {
if (item.get('default') === true) {
defaultOptions = defaultOptions.set(option, item.get('value'));
}
});
if (topology.has('options') && topology.get('options')) {
topology.get('options').forEach((option) => {
const optionId = option.get('id');
const defaultValue = option.get('defaultValue');
defaultOptions = defaultOptions.set(optionId, defaultValue);
});
}

Expand Down
85 changes: 52 additions & 33 deletions client/app/styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

@details-window-width: 420px;
@details-window-padding-left: 36px;
@border-radius: 4px;

@terminal-header-height: 34px;

Expand Down Expand Up @@ -259,7 +260,7 @@ h2 {
.btn-opacity;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
border-radius: @border-radius;
opacity: 0.8;
margin-bottom: 3px;

Expand Down Expand Up @@ -943,48 +944,76 @@ h2 {

.status {
text-transform: uppercase;
padding: 2px 12px;
border-radius: @border-radius;
color: @text-secondary-color;
display: inline-block;

&-icon {
font-size: 16px;
font-size: 1rem;
position: relative;
top: 1px;
margin-right: 0.5em;
top: 0.125rem;
margin-right: 0.25rem;
}

&.status-loading {
animation: status-loading 2.0s infinite ease-in-out;
animation: blinking 2.0s infinite ease-in-out;
text-transform: none;
color: @text-color;
}
}

.sidebar {
position: fixed;
bottom: 16px;
left: 16px;
font-size: .7rem;
.topology-options {

&-item {
.topology-option {
color: @text-secondary-color;
border-radius: 2px;
padding: 2px 8px;
width: 100%;
margin: 6px 0;

&.status {
padding: 4px 8px;
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}

&-wrapper {
border-radius: @border-radius;
border: 1px solid @background-darker-color;
display: inline-block;
}

&-action {
.btn-opacity;
text-transform: uppercase;
font-weight: bold;
color: darken(@weave-orange, 25%);
padding: 3px 12px;
cursor: pointer;
font-size: 90%;
margin-left: 0.5em;
opacity: @link-opacity-default;
display: inline-block;

&-selected, &:hover {
color: @text-darker-color;
background-color: @background-darker-color;
}

&-selected {
cursor: default;
}

&:first-child {
border-left: none;
border-top-left-radius: @border-radius;
border-bottom-left-radius: @border-radius;
}

&:last-child {
border-top-right-radius: @border-radius;
border-bottom-right-radius: @border-radius;
}
}
}

}

.sidebar {
position: fixed;
bottom: 16px;
left: 16px;
font-size: .7rem;
}

@keyframes blinking {
Expand All @@ -995,16 +1024,6 @@ h2 {
}
}

@keyframes status-loading {
0%, 100% {
background-color: @background-darker-secondary-color;
color: @text-secondary-color;
} 50% {
background-color: @background-darker-color;
color: @text-color;
}
}

//
// Debug panel!
//
Expand Down

0 comments on commit 88abeb7

Please sign in to comment.