Skip to content

Commit

Permalink
Merge pull request #505 from weaveworks/499-empty
Browse files Browse the repository at this point in the history
Show message if topology is empty
  • Loading branch information
tomwilkie committed Nov 3, 2015
2 parents 29c2f82 + 5c086ae commit 7fdc328
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 16 deletions.
31 changes: 26 additions & 5 deletions client/app/scripts/charts/nodes-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Edge = require('./edge');
const Naming = require('../constants/naming');
const NodesLayout = require('./nodes-layout');
const Node = require('./node');
const NodesError = require('./nodes-error');

const MARGINS = {
top: 130,
Expand Down Expand Up @@ -151,6 +152,27 @@ const NodesChart = React.createClass({
}, this);
},

renderMaxNodesError: function(show) {
return (
<NodesError faIconClass="fa-ban" hidden={!show}>
<div className="centered">Too many nodes to show in the browser.<br />We're working on it, but for now, try a different view?</div>
</NodesError>
);
},

renderEmptyTopologyError: function(show) {
return (
<NodesError faIconClass="fa-circle-thin" hidden={!show}>
<div className="heading">Nothing to show. This can have any of these reasons:</div>
<ul>
<li>We haven't received any reports from probes recently. Are the probes properly configured?</li>
<li>There are nodes, but they're currently hidden. Check the view options in the bottom-left if they allow for showing hidden nodes.</li>
<li>Containers view only: you're not running Docker, or you don't have any containers.</li>
</ul>
</NodesError>
);
},

render: function() {
const nodeElements = this.renderGraphNodes(this.state.nodes, this.state.nodeScale);
const edgeElements = this.renderGraphEdges(this.state.edges, this.state.nodeScale);
Expand All @@ -165,15 +187,14 @@ const NodesChart = React.createClass({
translate = shiftTranslate;
wasShifted = true;
}
const errorClassNames = this.state.maxNodesExceeded ? 'nodes-chart-error' : 'nodes-chart-error hide';
const svgClassNames = this.state.maxNodesExceeded || _.size(nodeElements) === 0 ? 'hide' : '';
const errorEmpty = this.renderEmptyTopologyError(AppStore.isTopologyEmpty());
const errorMaxNodesExceeded = this.renderMaxNodesError(this.state.maxNodesExceeded);

return (
<div className="nodes-chart">
<div className={errorClassNames}>
<span className="nodes-chart-error-icon fa fa-ban" />
<div>Too many nodes to show in the browser.<br />We're working on it, but for now, try a different view?</div>
</div>
{errorEmpty}
{errorMaxNodesExceeded}
<svg width="100%" height="100%" className={svgClassNames} onMouseUp={this.handleMouseUp}>
<Spring endValue={{val: translate, config: [80, 20]}}>
{function(interpolated) {
Expand Down
24 changes: 24 additions & 0 deletions client/app/scripts/charts/nodes-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const React = require('react');

const NodesError = React.createClass({

render: function() {
let classNames = 'nodes-chart-error';
if (this.props.hidden) {
classNames += ' hide';
}
let iconClassName = 'fa ' + this.props.faIconClass;

return (
<div className={classNames}>
<div className="nodes-chart-error-icon">
<span className={iconClassName} />
</div>
{this.props.children}
</div>
);
}

});

module.exports = NodesError;
32 changes: 30 additions & 2 deletions client/app/scripts/stores/__tests__/app-store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ describe('AppStore', function() {
topologyId: 'topo1'
};

const ClickTopology2Action = {
type: ActionTypes.CLICK_TOPOLOGY,
topologyId: 'topo2'
};

const ClickGroupingAction = {
type: ActionTypes.CLICK_GROUPING,
grouping: 'grouped'
Expand Down Expand Up @@ -112,10 +117,19 @@ describe('AppStore', function() {
{value: 'off', default: true}
]
},
stats: {
node_count: 1
},
sub_topologies: [{
url: '/topo1-grouped',
name: 'topo 1 grouped'
}]
}, {
url: '/topo2',
name: 'Topo2',
stats: {
node_count: 0
}
}]
};

Expand All @@ -142,7 +156,7 @@ describe('AppStore', function() {
registeredCallback(ClickTopologyAction);
registeredCallback(ReceiveTopologiesAction);

expect(AppStore.getTopologies().length).toBe(1);
expect(AppStore.getTopologies().length).toBe(2);
expect(AppStore.getCurrentTopology().name).toBe('Topo1');
expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1');
expect(AppStore.getCurrentTopologyOptions().option1).toBeDefined();
Expand All @@ -152,7 +166,7 @@ describe('AppStore', function() {
registeredCallback(ReceiveTopologiesAction);
registeredCallback(ClickSubTopologyAction);

expect(AppStore.getTopologies().length).toBe(1);
expect(AppStore.getTopologies().length).toBe(2);
expect(AppStore.getCurrentTopology().name).toBe('topo 1 grouped');
expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1-grouped');
expect(AppStore.getCurrentTopologyOptions()).toBeUndefined();
Expand Down Expand Up @@ -312,4 +326,18 @@ describe('AppStore', function() {
expect(AppStore.getAdjacentNodes().size).toEqual(0);
});

// empty topology

it('detects that the topology is empty', function() {
registeredCallback(ReceiveTopologiesAction);
registeredCallback(ClickTopologyAction);
expect(AppStore.isTopologyEmpty()).toBeFalsy();

registeredCallback(ClickTopology2Action);
expect(AppStore.isTopologyEmpty()).toBeTruthy();

registeredCallback(ClickTopologyAction);
expect(AppStore.isTopologyEmpty()).toBeFalsy();
});

});
17 changes: 13 additions & 4 deletions client/app/scripts/stores/app-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ const AppStore = assign({}, EventEmitter.prototype, {
return topologiesLoaded;
},

isTopologyEmpty: function() {
return currentTopology && currentTopology.stats && currentTopology.stats.node_count === 0 && nodes.size === 0;
},

isWebsocketClosed: function() {
return websocketClosed;
}
Expand Down Expand Up @@ -310,10 +314,15 @@ AppStore.registeredCallback = function(payload) {
break;

case ActionTypes.RECEIVE_NODES_DELTA:
debug('RECEIVE_NODES_DELTA',
'remove', _.size(payload.delta.remove),
'update', _.size(payload.delta.update),
'add', _.size(payload.delta.add));
const emptyMessage = !payload.delta.add && !payload.delta.remove
&& payload.delta.update;

if (!emptyMessage) {
debug('RECEIVE_NODES_DELTA',
'remove', _.size(payload.delta.remove),
'update', _.size(payload.delta.update),
'add', _.size(payload.delta.add));
}

errorUrl = null;

Expand Down
5 changes: 1 addition & 4 deletions client/app/scripts/utils/web-api-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ function createWebsocket(topologyUrl, optionsQuery) {

socket.onmessage = function(event) {
const msg = JSON.parse(event.data);
if (msg.add || msg.remove || msg.update) {
AppActions.receiveNodesDelta(msg);
}
AppActions.receiveNodesDelta(msg);
};
}

Expand Down Expand Up @@ -146,4 +144,3 @@ module.exports = {

getNodesDelta: getTopology
};

7 changes: 6 additions & 1 deletion client/app/styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,15 @@ h2 {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: @text-secondary-color;
width: 33%;

.heading {
font-size: 125%;
}

&-icon {
text-align: center;
opacity: 0.25;
font-size: 320px;
}
Expand Down

0 comments on commit 7fdc328

Please sign in to comment.